transform.alc 16 KB

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