transform.alc 20 KB

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