transform.alc 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  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. include "library.alh"
  8. Element function make_matching_schedule(schedule_model : Element, LHS : String, ignore : Element):
  9. Element schedule
  10. Element workset
  11. Element all_elements
  12. Element full_all_elements
  13. Integer required_size
  14. String new_element
  15. Integer counter
  16. String next
  17. Element tmp
  18. // Initialize
  19. schedule = create_node()
  20. workset = create_node()
  21. all_elements = allAssociationDestinations(schedule_model, LHS, "LHS_contains")
  22. full_all_elements = set_copy(all_elements)
  23. required_size = read_nr_out(all_elements)
  24. // Need to keep adding to the schedule
  25. while (read_nr_out(schedule) < required_size):
  26. // workset is empty, but we still need to add to the list
  27. // Therefore, we pick a random, unbound node, and add it to the workset
  28. new_element = set_pop(all_elements)
  29. while (bool_or(set_in(schedule, new_element), is_edge(schedule_model["model"][new_element]))):
  30. // Element is not usable, so pick another one
  31. new_element = set_pop(all_elements)
  32. set_add(workset, new_element)
  33. // Handle the workset
  34. while (read_nr_out(workset) > 0):
  35. // Still elements in the workset, so pop from these first
  36. next = set_pop(workset)
  37. // Check if element might not be already used somewhere
  38. if (bool_not(set_in(schedule, next))):
  39. if (set_in(full_all_elements, next)):
  40. if (bool_not(set_in(ignore, read_attribute(schedule_model, next, "label")))):
  41. list_append(schedule, next)
  42. required_size = required_size - 1
  43. // If it is an edge, we should also add the target and source
  44. if (is_edge(schedule_model["model"][next])):
  45. // Add the target/source to the schedule
  46. set_add(workset, reverseKeyLookup(schedule_model["model"], read_edge_src(schedule_model["model"][next])))
  47. set_add(workset, reverseKeyLookup(schedule_model["model"], read_edge_dst(schedule_model["model"][next])))
  48. // Also add all outgoing links
  49. counter = read_nr_out(schedule_model["model"][next])
  50. while (counter > 0):
  51. counter = counter - 1
  52. if (set_in_node(schedule_model["model"], read_out(schedule_model["model"][next], counter))):
  53. set_add(workset, reverseKeyLookup(schedule_model["model"], read_out(schedule_model["model"][next], counter)))
  54. return schedule!
  55. Element function get_possible_bindings(host_model : Element, schedule_model : Element, current_element : String, map : Element):
  56. Element options
  57. String src_label
  58. String dst_label
  59. String typename
  60. String original_typename
  61. options = create_node()
  62. typename = read_type(schedule_model, current_element)
  63. original_typename = string_substr(typename, 4, string_len(typename))
  64. if (is_edge(schedule_model["model"][current_element])):
  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. if (bool_and(set_in(dict_keys(map), src_label), set_in(dict_keys(map), dst_label))):
  69. // Source and destination are bound
  70. options = allOutgoingAssociationInstances(host_model, map[src_label], original_typename)
  71. options = set_overlap(options, allIncomingAssociationInstances(host_model, map[dst_label], original_typename))
  72. elif (set_in(dict_keys(map), src_label)):
  73. // Source is bound
  74. options = allOutgoingAssociationInstances(host_model, map[src_label], original_typename)
  75. elif (set_in(dict_keys(map), dst_label)):
  76. // Destination is bound
  77. options = allIncomingAssociationInstances(host_model, map[dst_label], original_typename)
  78. else:
  79. // Neither is bound, so just get all of them
  80. return allInstances(host_model, original_typename)!
  81. else:
  82. // Is a node, so find whether or not there are some connections that are already resolved
  83. Element ic
  84. Element oc
  85. String poll
  86. ic = allIncomingAssociationInstances(schedule_model, current_element, "PreElement")
  87. oc = allOutgoingAssociationInstances(schedule_model, current_element, "PreElement")
  88. while (bool_and(read_nr_out(ic) > 0, read_nr_out(options) == 0)):
  89. poll = set_pop(ic)
  90. if (dict_in(map, read_attribute(schedule_model, poll, "label"))):
  91. // This incoming link is already defined, so we just have one option: the destination of the link we matched
  92. set_add(options, readAssociationDestination(host_model, map[read_attribute(schedule_model, poll, "label")]))
  93. while (bool_and(read_nr_out(oc) > 0, read_nr_out(options) == 0)):
  94. poll = set_pop(oc)
  95. if (dict_in(map, read_attribute(schedule_model, poll, "label"))):
  96. // This incoming link is already defined, so we just have one option: the destination of the link we matched
  97. set_add(options, readAssociationSource(host_model, map[read_attribute(schedule_model, poll, "label")]))
  98. if (read_nr_out(options) == 0):
  99. // Is a node and no connections, so we just pick all options
  100. options = allInstances(host_model, original_typename)
  101. elif (read_nr_out(options) > 1):
  102. // Multiple "only" options, which will not work out: no options!
  103. log(" multi!")
  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. log("1")
  125. func = get_func_AL_model(import_node(constraint_function))
  126. result = func(host_model, option)
  127. if (result):
  128. set_add(filtered_options, option)
  129. Element attributes
  130. String attribute
  131. Element value
  132. Element func
  133. Boolean result
  134. Element attributes_copy
  135. options = filtered_options
  136. filtered_options = create_node()
  137. // Check whether all attributes have a satisfied condition
  138. attributes_copy = dict_keys(getAttributeList(schedule_model, current_element))
  139. while (read_nr_out(options) > 0):
  140. option = set_pop(options)
  141. attributes = set_copy(attributes_copy)
  142. result = True
  143. while (read_nr_out(attributes) > 0):
  144. attribute = set_pop(attributes)
  145. if (bool_not(string_startswith(attribute, "constraint_"))):
  146. continue!
  147. value = read_attribute(schedule_model, current_element, attribute)
  148. // Attribute might be undefined, so skip if it is
  149. if (element_neq(value, read_root())):
  150. log("2")
  151. func = get_func_AL_model(import_node(value))
  152. result = func(read_attribute(host_model, option, string_substr(attribute, string_len("constraint_"), string_len(attribute) + 1)))
  153. else:
  154. result = True
  155. if (bool_not(result)):
  156. break!
  157. // Check value of last result, which will be True if all passed, or False otherwise
  158. if (result):
  159. set_add(filtered_options, option)
  160. options = filtered_options
  161. return options!
  162. Element function full_match(host_model : Element, schedule_model : Element, current : String):
  163. Element NACs
  164. String LHS
  165. String NAC
  166. Element mappings
  167. Element mapping
  168. Integer i
  169. Element result
  170. Element final_mappings
  171. Boolean allowed
  172. final_mappings = create_node()
  173. // First match the LHS part itself to get initial mappings
  174. LHS = set_pop(allAssociationDestinations(schedule_model, current, "LHSLink"))
  175. mappings = match(host_model, schedule_model, LHS, create_node())
  176. // Got a list of all possible mappings, now filter based on NACs
  177. NACs = allAssociationDestinations(schedule_model, current, "NACLink")
  178. // For each possible mapping, we check all NACs!
  179. while (read_nr_out(mappings) > 0):
  180. mapping = set_pop(mappings)
  181. i = 0
  182. allowed = True
  183. while (i < read_nr_out(NACs)):
  184. NAC = read_edge_dst(read_out(NACs, i))
  185. result = match(host_model, schedule_model, NAC, mapping)
  186. if (read_nr_out(result) > 0):
  187. // NAC could be matched, and therefore is not allowed
  188. allowed = False
  189. break!
  190. i = i + 1
  191. if (allowed):
  192. set_add(final_mappings, mapping)
  193. return final_mappings!
  194. Element function match(host_model : Element, schedule_model : Element, LHS : String, initial_mapping : Element):
  195. // Match the schedule_model to the host_model, returning all possible mappings from schedule_model elements to host_model elements
  196. // Make the schedule first
  197. Element schedule
  198. schedule = make_matching_schedule(schedule_model, LHS, dict_keys(initial_mapping))
  199. // Now follow the schedule, incrementally building all mappings
  200. Element mappings
  201. Element new_mappings
  202. Element new_map
  203. String current_element
  204. Element map
  205. String option
  206. Element options
  207. mappings = create_node()
  208. set_add(mappings, initial_mapping)
  209. while (bool_and(read_nr_out(schedule) > 0, read_nr_out(mappings) > 0)):
  210. current_element = list_pop(schedule, 0)
  211. new_mappings = create_node()
  212. while (read_nr_out(mappings) > 0):
  213. map = set_pop(mappings)
  214. options = get_possible_bindings(host_model, schedule_model, current_element, map)
  215. while (read_nr_out(options) > 0):
  216. option = set_pop(options)
  217. new_map = dict_copy(map)
  218. dict_add(new_map, read_attribute(schedule_model, current_element, "label"), option)
  219. set_add(new_mappings, new_map)
  220. mappings = new_mappings
  221. // Finished, so try the global constraint
  222. String constraint
  223. Element func
  224. Boolean result
  225. new_mappings = create_node()
  226. constraint = read_attribute(schedule_model, LHS, "constraint")
  227. if (element_neq(constraint, read_root())):
  228. while (read_nr_out(mappings) > 0):
  229. map = set_pop(mappings)
  230. log("3")
  231. func = get_func_AL_model(import_node(constraint))
  232. result = func(host_model, map)
  233. if (result):
  234. set_add(new_mappings, map)
  235. mappings = new_mappings
  236. return mappings!
  237. Void function rewrite(host_model : Element, schedule_model : Element, RHS : String, mapping : Element):
  238. // Rewrite the host model based on the mapping combined with the RHS
  239. Element LHS_labels
  240. Element RHS_labels
  241. Element RHS_elements
  242. Element remaining
  243. String elem
  244. String label
  245. Element labels_to_remove
  246. Element labels_to_add
  247. String typename
  248. String original_typename
  249. String src
  250. String dst
  251. Element new_mapping
  252. String new_name
  253. Element RHS_map
  254. String tmp
  255. Element value
  256. Element action
  257. Element original_RHS_labels
  258. LHS_labels = dict_keys(mapping)
  259. RHS_labels = create_node()
  260. RHS_map = create_node()
  261. RHS_elements = allAssociationDestinations(schedule_model, RHS, "RHS_contains")
  262. while (read_nr_out(RHS_elements) > 0):
  263. tmp = set_pop(RHS_elements)
  264. label = read_attribute(schedule_model, tmp, "label")
  265. set_add(RHS_labels, label)
  266. dict_add(RHS_map, label, tmp)
  267. remaining = set_overlap(LHS_labels, RHS_labels)
  268. original_RHS_labels = set_copy(RHS_labels)
  269. while (read_nr_out(remaining) > 0):
  270. elem = set_pop(remaining)
  271. set_remove(LHS_labels, elem)
  272. set_remove(RHS_labels, elem)
  273. labels_to_remove = LHS_labels
  274. labels_to_add = set_to_list(RHS_labels)
  275. new_mapping = dict_copy(mapping)
  276. while (read_nr_out(labels_to_add) > 0):
  277. // Add the elements linked to these labels
  278. label = list_pop(labels_to_add, 0)
  279. if (is_edge(schedule_model["model"][RHS_map[label]])):
  280. // Edge
  281. src = read_attribute(schedule_model, reverseKeyLookup(schedule_model["model"], read_edge_src(schedule_model["model"][RHS_map[label]])), "label")
  282. dst = read_attribute(schedule_model, reverseKeyLookup(schedule_model["model"], read_edge_dst(schedule_model["model"][RHS_map[label]])), "label")
  283. // First check whether both source and destination are already created
  284. if (bool_and(dict_in(new_mapping, src), dict_in(new_mapping, dst))):
  285. // Both are present, so we can make the link
  286. typename = read_type(schedule_model, RHS_map[label])
  287. original_typename = string_substr(typename, 5, string_len(typename))
  288. new_name = instantiate_link(host_model, original_typename, "", new_mapping[src], new_mapping[dst])
  289. dict_add(new_mapping, label, new_name)
  290. else:
  291. // Delay this a bit, until all are bound
  292. list_append(labels_to_add, label)
  293. else:
  294. // Node
  295. // Create the node and add it
  296. typename = read_type(schedule_model, RHS_map[label])
  297. original_typename = string_substr(typename, 5, string_len(typename))
  298. new_name = instantiate_node(host_model, original_typename, "")
  299. dict_add(new_mapping, label, new_name)
  300. Element attributes
  301. String attribute
  302. Element result
  303. Element func
  304. while (read_nr_out(original_RHS_labels) > 0):
  305. // Perform actions
  306. label = set_pop(original_RHS_labels)
  307. // Do all attribute actions that are defined
  308. attributes = dict_keys(getAttributeList(schedule_model, RHS_map[label]))
  309. while (read_nr_out(attributes) > 0):
  310. attribute = set_pop(attributes)
  311. if (bool_not(string_startswith(attribute, "value_"))):
  312. continue!
  313. value = read_attribute(schedule_model, RHS_map[label], attribute)
  314. if (element_neq(value, read_root())):
  315. log("4")
  316. func = get_func_AL_model(import_node(value))
  317. result = func(host_model, new_mapping[label], mapping)
  318. if (has_value(result)):
  319. // New value defined, so assign!
  320. instantiate_attribute(host_model, new_mapping[label], string_substr(attribute, string_len("value_"), string_len(attribute) + 1), result)
  321. else:
  322. // Non-value return means to destroy the attribute!
  323. unset_attribute(host_model, new_mapping[label], string_substr(attribute, string_len("value_"), string_len(attribute) + 1))
  324. // Do the global action of each element
  325. action = read_attribute(schedule_model, RHS_map[label], "action")
  326. if (element_neq(action, read_root())):
  327. Element func
  328. log("5")
  329. log("Got action: " + cast_e2s(action))
  330. log("After import: " + cast_e2s(import_node(action)))
  331. func = get_func_AL_model(import_node(action))
  332. func(host_model, new_mapping[label], mapping)
  333. while (read_nr_out(labels_to_remove) > 0):
  334. // Remove the elements linked to these labels
  335. label = set_pop(labels_to_remove)
  336. model_delete_element(host_model, mapping[label])
  337. dict_delete(new_mapping, label)
  338. // Execute global action (whatever it may be)
  339. action = read_attribute(schedule_model, RHS, "action")
  340. if (element_neq(action, read_root())):
  341. Element func
  342. log("6")
  343. func = get_func_AL_model(import_node(action))
  344. func(host_model, new_mapping)
  345. return!
  346. Element function transform(host_model : Element, schedule_model : Element):
  347. // Find initial model
  348. Element all_composites
  349. String composite
  350. String current
  351. Element merged
  352. Element original_mm
  353. // Now start transforming for real
  354. all_composites = allInstances(schedule_model, "Composite")
  355. if (read_nr_out(all_composites) == 1):
  356. // Only one, so it is easy
  357. current = set_pop(all_composites)
  358. else:
  359. // Filter out those that are themselves contained
  360. while (read_nr_out(all_composites) > 0):
  361. composite = set_pop(all_composites)
  362. if (read_nr_out(allIncomingAssociationInstances(schedule_model, composite, "Contains")) == 0):
  363. // Isn't contained in any, so this is the root model!
  364. current = composite
  365. if (transform_composite(host_model, schedule_model, current)):
  366. // Success, so return True if it is in-place, or the new model if it is out-place
  367. return True!
  368. else:
  369. return False!
  370. Boolean function transform_composite(host_model : Element, schedule_model : Element, composite : String):
  371. String current
  372. String typename
  373. Boolean result
  374. current = set_pop(allAssociationDestinations(schedule_model, composite, "Initial"))
  375. while (is_nominal_instance(schedule_model, current, "Rule")):
  376. // Still a rule that we must execute
  377. typename = read_type(schedule_model, current)
  378. if (typename == "Atomic"):
  379. result = transform_atomic(host_model, schedule_model, current)
  380. elif (typename == "Query"):
  381. result = transform_query(host_model, schedule_model, current)
  382. elif (typename == "Composite"):
  383. result = transform_composite(host_model, schedule_model, current)
  384. elif (typename == "ForAll"):
  385. result = transform_forall(host_model, schedule_model, current)
  386. if (result):
  387. current = set_pop(allAssociationDestinations(schedule_model, current, "OnSuccess"))
  388. else:
  389. current = set_pop(allAssociationDestinations(schedule_model, current, "OnFailure"))
  390. // No longer a rule, so it is either success or failure
  391. if (is_nominal_instance(schedule_model, current, "Success")):
  392. return True!
  393. else:
  394. return False!
  395. Boolean function transform_atomic(host_model : Element, schedule_model : Element, current : String):
  396. // Execute the atomic transformation
  397. Element mappings
  398. Element mapping
  399. mappings = full_match(host_model, schedule_model, current)
  400. if (read_nr_out(mappings) > 0):
  401. // Pick one!
  402. mapping = random_choice(set_to_list(mappings))
  403. String RHS
  404. RHS = set_pop(allAssociationDestinations(schedule_model, current, "RHSLink"))
  405. rewrite(host_model, schedule_model, RHS, mapping)
  406. return True!
  407. else:
  408. return False!
  409. Boolean function transform_forall(host_model : Element, schedule_model : Element, current : String):
  410. // Execute the atomic transformation
  411. Element mappings
  412. String RHS
  413. Element mapping
  414. Boolean result
  415. mappings = full_match(host_model, schedule_model, current)
  416. if (read_nr_out(mappings) > 0):
  417. result = True
  418. else:
  419. result = False
  420. while (read_nr_out(mappings) > 0):
  421. mapping = set_pop(mappings)
  422. // TODO check if there are actually no deletions happening in the meantime of other matched elements...
  423. RHS = set_pop(allAssociationDestinations(schedule_model, current, "RHSLink"))
  424. rewrite(host_model, schedule_model, RHS, mapping)
  425. return result!
  426. Boolean function transform_query(host_model : Element, schedule_model : Element, current : String):
  427. // Execute the transformation
  428. Element mappings
  429. Element mapping
  430. mappings = full_match(host_model, schedule_model, current)
  431. if (read_nr_out(mappings) > 0):
  432. return True!
  433. else:
  434. return False!