transform.alc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. include "primitives.alh"
  2. include "object_operations.alh"
  3. include "modelling.alh"
  4. include "random.alh"
  5. include "conformance_scd.alh"
  6. Element function make_matching_schedule(schedule_model : Element, LHS : Element):
  7. Element schedule
  8. Element workset
  9. Element all_elements
  10. Element full_all_elements
  11. Integer required_size
  12. String new_element
  13. Integer counter
  14. String next
  15. Element tmp
  16. // Initialize
  17. schedule = create_node()
  18. workset = create_node()
  19. all_elements = allAssociationDestinations(schedule_model, LHS, "LHS_contains")
  20. full_all_elements = set_copy(all_elements)
  21. required_size = read_nr_out(all_elements)
  22. // Need to keep adding to the schedule
  23. while (read_nr_out(schedule) < required_size):
  24. // workset is empty, but we still need to add to the list
  25. // Therefore, we pick a random, unbound node, and add it to the workset
  26. new_element = set_pop(all_elements)
  27. while (bool_or(set_in(schedule, new_element), is_edge(schedule_model["model"][new_element]))):
  28. // Element is not usable, so pick another one
  29. new_element = set_pop(all_elements)
  30. set_add(workset, new_element)
  31. // Handle the workset
  32. while (read_nr_out(workset) > 0):
  33. // Still elements in the workset, so pop from these first
  34. next = set_pop(workset)
  35. // Check if element might not be already used somewhere
  36. if (bool_not(set_in(schedule, next))):
  37. if (set_in(full_all_elements, next)):
  38. list_append(schedule, next)
  39. // If it is an edge, we should also add the target and source
  40. if (is_edge(schedule_model["model"][next])):
  41. // Add the target/source to the schedule
  42. set_add(workset, reverseKeyLookup(schedule_model["model"], read_edge_src(schedule_model["model"][next])))
  43. set_add(workset, reverseKeyLookup(schedule_model["model"], read_edge_dst(schedule_model["model"][next])))
  44. // Also add all outgoing links
  45. counter = read_nr_out(schedule_model["model"][next])
  46. while (counter > 0):
  47. counter = counter - 1
  48. if (set_in_node(schedule_model["model"], read_out(schedule_model["model"][next], counter))):
  49. set_add(workset, reverseKeyLookup(schedule_model["model"], read_out(schedule_model["model"][next], counter)))
  50. return schedule!
  51. Element function get_possible_bindings(host_model : Element, schedule_model : Element, current_element : String, map : Element):
  52. Element options
  53. String src_label
  54. String dst_label
  55. String typename
  56. String original_typename
  57. options = create_node()
  58. typename = reverseKeyLookup(schedule_model["metamodel"]["model"], dict_read_node(schedule_model["type_mapping"], schedule_model["model"][current_element]))
  59. original_typename = string_substr(typename, 4, string_len(typename))
  60. if (is_edge(schedule_model["model"][current_element])):
  61. // Is an edge, so check for already bound source/target
  62. src_label = read_attribute(schedule_model, reverseKeyLookup(schedule_model["model"], read_edge_src(schedule_model["model"][current_element])), "label")
  63. dst_label = read_attribute(schedule_model, reverseKeyLookup(schedule_model["model"], read_edge_dst(schedule_model["model"][current_element])), "label")
  64. if (bool_and(set_in(dict_keys(map), src_label), set_in(dict_keys(map), dst_label))):
  65. // Source and destination are bound
  66. options = allOutgoingAssociationInstances(host_model, map[src_label], original_typename)
  67. options = set_overlap(options, allIncomingAssociationInstances(host_model, map[dst_label], original_typename))
  68. elif (set_in(dict_keys(map), src_label)):
  69. // Source is bound
  70. options = allOutgoingAssociationInstances(host_model, map[src_label], original_typename)
  71. elif (set_in(dict_keys(map), dst_label)):
  72. // Destination is bound
  73. options = allIncomingAssociationInstances(host_model, map[dst_label], original_typename)
  74. else:
  75. // Neither is bound, so just get all of them
  76. log("ERROR: unbound source/target for association!")
  77. return create_node()!
  78. else:
  79. // Is a node, so check for already bound incoming/outgoing
  80. options = allInstances(host_model, original_typename)
  81. // Filter options further
  82. Element filtered_options
  83. String option
  84. filtered_options = create_node()
  85. while (read_nr_out(options) > 0):
  86. option = set_pop(options)
  87. // Check for detecting same element twice
  88. if (bool_not(set_in(map, option))):
  89. // Option is already present with another label, so skip this!
  90. // Check for local constraints of element
  91. if (element_eq(read_attribute(schedule_model, current_element, "constraint"), read_root())):
  92. // No local constraints, so all is well
  93. set_add(filtered_options, option)
  94. else:
  95. // Check local constraints and add only if positive
  96. Element constraint_function
  97. constraint_function = read_attribute(schedule_model, current_element, "constraint")
  98. Boolean result
  99. result = constraint_function(host_model, option)
  100. if (result):
  101. set_add(filtered_options, option)
  102. return filtered_options!
  103. Element function match(host_model : Element, schedule_model : Element, LHS : Element):
  104. // Match the schedule_model to the host_model, returning all possible mappings from schedule_model elements to host_model elements
  105. // Make the schedule first
  106. Element schedule
  107. schedule = make_matching_schedule(schedule_model, LHS)
  108. // Now follow the schedule, incrementally building all mappings
  109. Element mappings
  110. Element new_mappings
  111. Element new_map
  112. String current_element
  113. Element map
  114. String option
  115. Element options
  116. mappings = create_node()
  117. set_add(mappings, create_node())
  118. while (bool_and(read_nr_out(schedule) > 0, read_nr_out(mappings) > 0)):
  119. current_element = list_pop(schedule, 0)
  120. new_mappings = create_node()
  121. while (read_nr_out(mappings) > 0):
  122. map = set_pop(mappings)
  123. options = get_possible_bindings(host_model, schedule_model, current_element, map)
  124. while (read_nr_out(options) > 0):
  125. option = set_pop(options)
  126. new_map = dict_copy(map)
  127. dict_add(new_map, read_attribute(schedule_model, current_element, "label"), option)
  128. set_add(new_mappings, new_map)
  129. mappings = new_mappings
  130. // Finished, so try the global constraint
  131. Element constraint
  132. new_mappings = create_node()
  133. constraint = read_attribute(schedule_model, LHS, "constraint")
  134. if (element_neq(constraint, read_root())):
  135. while (read_nr_out(mappings) > 0):
  136. map = set_pop(mappings)
  137. if (constraint(host_model, map)):
  138. set_add(new_mappings, map)
  139. mappings = new_mappings
  140. return mappings!
  141. Void function rewrite(host_model : Element, schedule_model : Element, RHS : String, mapping : Element):
  142. // Rewrite the host model based on the mapping combined with the RHS
  143. Element LHS_labels
  144. Element RHS_labels
  145. Element RHS_elements
  146. Element remaining
  147. String elem
  148. String label
  149. Element labels_to_remove
  150. Element labels_to_add
  151. String typename
  152. String original_typename
  153. String src
  154. String dst
  155. Element new_mapping
  156. String new_name
  157. Element RHS_map
  158. String tmp
  159. Element value
  160. Element value_function
  161. Element action
  162. Element original_RHS_labels
  163. LHS_labels = dict_keys(mapping)
  164. RHS_labels = create_node()
  165. RHS_map = create_node()
  166. RHS_elements = allAssociationDestinations(schedule_model, RHS, "RHS_contains")
  167. while (read_nr_out(RHS_elements) > 0):
  168. tmp = set_pop(RHS_elements)
  169. label = read_attribute(schedule_model, tmp, "label")
  170. set_add(RHS_labels, label)
  171. dict_add(RHS_map, label, tmp)
  172. remaining = set_overlap(LHS_labels, RHS_labels)
  173. original_RHS_labels = set_copy(RHS_labels)
  174. while (read_nr_out(remaining) > 0):
  175. elem = set_pop(remaining)
  176. set_remove(LHS_labels, elem)
  177. set_remove(RHS_labels, elem)
  178. labels_to_remove = LHS_labels
  179. labels_to_add = set_to_list(RHS_labels)
  180. new_mapping = dict_copy(mapping)
  181. while (read_nr_out(labels_to_add) > 0):
  182. // Add the elements linked to these labels
  183. label = list_pop(labels_to_add, 0)
  184. if (element_neq(read_attribute(schedule_model, RHS_map[label], "value"), read_root())):
  185. // There is a value associated with this node
  186. value_function = read_attribute(schedule_model, RHS_map[label], "value")
  187. value = value_function(host_model, mapping)
  188. typename = reverseKeyLookup(schedule_model["metamodel"]["model"], dict_read_node(schedule_model["type_mapping"], schedule_model["model"][RHS_map[label]]))
  189. original_typename = string_substr(typename, 5, string_len(typename))
  190. new_name = instantiate_value(host_model, original_typename, "", value)
  191. dict_add(new_mapping, label, new_name)
  192. elif (is_edge(schedule_model["model"][RHS_map[label]])):
  193. // Edge
  194. src = read_attribute(schedule_model, reverseKeyLookup(schedule_model["model"], read_edge_src(schedule_model["model"][RHS_map[label]])), "label")
  195. dst = read_attribute(schedule_model, reverseKeyLookup(schedule_model["model"], read_edge_dst(schedule_model["model"][RHS_map[label]])), "label")
  196. // First check whether both source and destination are already created
  197. if (bool_and(dict_in(new_mapping, src), dict_in(new_mapping, dst))):
  198. // Both are present, so we can make the link
  199. typename = reverseKeyLookup(schedule_model["metamodel"]["model"], dict_read_node(schedule_model["type_mapping"], schedule_model["model"][RHS_map[label]]))
  200. original_typename = string_substr(typename, 5, string_len(typename))
  201. new_name = instantiate_link(host_model, original_typename, "", new_mapping[src], new_mapping[dst])
  202. dict_add(new_mapping, label, new_name)
  203. else:
  204. // Delay this a bit, until all are bound
  205. list_append(labels_to_add, label)
  206. else:
  207. // Node
  208. // Create the node and add it
  209. typename = reverseKeyLookup(schedule_model["metamodel"]["model"], dict_read_node(schedule_model["type_mapping"], schedule_model["model"][RHS_map[label]]))
  210. original_typename = string_substr(typename, 5, string_len(typename))
  211. new_name = instantiate_node(host_model, original_typename, "")
  212. dict_add(new_mapping, label, new_name)
  213. while (read_nr_out(original_RHS_labels) > 0):
  214. label = set_pop(original_RHS_labels)
  215. action = read_attribute(schedule_model, RHS_map[label], "action")
  216. if (element_neq(action, read_root())):
  217. action(host_model, new_mapping[label], mapping)
  218. while (read_nr_out(labels_to_remove) > 0):
  219. // Remove the elements linked to these labels
  220. label = set_pop(labels_to_remove)
  221. model_delete_element(host_model, mapping[label])
  222. dict_delete(new_mapping, label)
  223. // Execute global action (whatever it may be)
  224. action = read_attribute(schedule_model, RHS, "action")
  225. if (element_neq(action, read_root())):
  226. action(host_model, new_mapping)
  227. return!
  228. Boolean function transform(host_model : Element, schedule_model : Element):
  229. // Find initial model
  230. Element all_composites
  231. String composite
  232. String current
  233. all_composites = allInstances(schedule_model, "Composite")
  234. if (read_nr_out(all_composites) == 1):
  235. // Only one, so it is easy
  236. current = set_pop(all_composites)
  237. else:
  238. // Filter out those that are themselves contained
  239. while (read_nr_out(all_composites) > 0):
  240. composite = set_pop(all_composites)
  241. if (read_nr_out(allIncomingAssociationInstances(schedule_model, composite, "Contains")) == 0):
  242. // Isn't contained in any, so this is the root model!
  243. current = composite
  244. return transform_composite(host_model, schedule_model, current)!
  245. Boolean function transform_composite(host_model : Element, schedule_model : Element, composite : String):
  246. String current
  247. String typename
  248. Boolean result
  249. current = set_pop(allAssociationDestinations(schedule_model, composite, "Initial"))
  250. while (is_nominal_instance(schedule_model, current, "Rule")):
  251. // Still a rule that we must execute
  252. typename = reverseKeyLookup(schedule_model["metamodel"]["model"], dict_read_node(schedule_model["type_mapping"], schedule_model["model"][current]))
  253. if (typename == "Atomic"):
  254. result = transform_atomic(host_model, schedule_model, current)
  255. elif (typename == "Query"):
  256. result = transform_query(host_model, schedule_model, current)
  257. elif (typename == "Composite"):
  258. result = transform_composite(host_model, schedule_model, current)
  259. if (result):
  260. current = set_pop(allAssociationDestinations(schedule_model, current, "OnSuccess"))
  261. else:
  262. current = set_pop(allAssociationDestinations(schedule_model, current, "OnFailure"))
  263. // No longer a rule, so it is either success or failure
  264. if (is_nominal_instance(schedule_model, current, "Success")):
  265. return True!
  266. else:
  267. return False!
  268. Boolean function transform_atomic(host_model : Element, schedule_model : Element, current : String):
  269. // Execute the atomic transformation
  270. Element mappings
  271. String LHS
  272. Element mapping
  273. LHS = set_pop(allAssociationDestinations(schedule_model, current, "AtomicLHS"))
  274. mappings = match(host_model, schedule_model, LHS)
  275. if (read_nr_out(mappings) > 0):
  276. // Pick one!
  277. mapping = random_choice(set_to_list(mappings))
  278. String RHS
  279. RHS = set_pop(allAssociationDestinations(schedule_model, current, "AtomicRHS"))
  280. rewrite(host_model, schedule_model, RHS, mapping)
  281. return True!
  282. else:
  283. return False!
  284. Boolean function transform_query(host_model : Element, schedule_model : Element, current : String):
  285. // Execute the transformation
  286. String LHS
  287. Element mappings
  288. Element mapping
  289. LHS = set_pop(allAssociationDestinations(schedule_model, current, "QueryLHS"))
  290. mappings = match(host_model, schedule_model, LHS)
  291. if (read_nr_out(mappings) > 0):
  292. return True!
  293. else:
  294. return False!