transform.alc 19 KB

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