transform.alc 19 KB

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