transform.alc 16 KB

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