transform.alc 19 KB

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