123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598 |
- include "primitives.alh"
- include "object_operations.alh"
- include "modelling.alh"
- include "random.alh"
- include "conformance_scd.alh"
- include "model_management.alh"
- include "library.alh"
- Element function make_matching_schedule(schedule_model : Element, LHS : String, ignore : Element):
- Element schedule
- Element workset
- Element all_elements
- Element full_all_elements
- Integer required_size
- String new_element
- Integer counter
- String next
- Element tmp
- String elem_id
- Element scheduled
- Element reverse
- reverse = make_reverse_dictionary(schedule_model["model"])
- // Initialize
- schedule = list_create()
- scheduled = set_create()
- workset = set_create()
- all_elements = allAssociationDestinations(schedule_model, LHS, "LHS_contains")
- full_all_elements = set_copy(all_elements)
- required_size = set_len(all_elements)
- // Need to keep adding to the schedule
- while (list_len(schedule) < required_size):
- // workset is empty, but we still need to add to the list
- // Therefore, we pick a random, unbound node, and add it to the workset
- new_element = set_pop(all_elements)
- while (bool_or(set_in(scheduled, new_element), is_edge(schedule_model["model"][new_element]))):
- // Element is not usable, so pick another one
- new_element = set_pop(all_elements)
- set_add(workset, new_element)
- // Handle the workset
- while (set_len(workset) > 0):
- // Still elements in the workset, so pop from these first
- next = set_pop(workset)
- // Check if element might not be already used somewhere
- if (bool_not(set_in(scheduled, next))):
- if (set_in(full_all_elements, next)):
- if (bool_not(set_in(ignore, read_attribute(schedule_model, next, "label")))):
- list_insert(schedule, next, 0)
- set_add(scheduled, next)
- else:
- required_size = required_size - 1
- continue!
- // If it is an edge, we should also add the target and source
- if (is_edge(schedule_model["model"][next])):
- // Add the target/source to the schedule
- set_add(workset, reverse[cast_id(read_edge_src(schedule_model["model"][next]))])
- set_add(workset, reverse[cast_id(read_edge_dst(schedule_model["model"][next]))])
- // Also add all outgoing links
- counter = read_nr_out(schedule_model["model"][next])
- while (counter > 0):
- counter = counter - 1
- elem_id = cast_id(read_out(schedule_model["model"][next], counter))
- if (dict_in(reverse, elem_id)):
- set_add(workset, reverse[elem_id])
- // And incoming links
- counter = read_nr_in(schedule_model["model"][next])
- while (counter > 0):
- counter = counter - 1
- elem_id = cast_id(read_in(schedule_model["model"][next], counter))
- if (dict_in(reverse, elem_id)):
- set_add(workset, reverse[elem_id])
- return schedule!
- Element function get_possible_bindings(host_model : Element, schedule_model : Element, current_element : String, map : Element):
- Element options
- String src_label
- String dst_label
- String typename
- String original_typename
- Boolean guaranteed_instance
- options = set_create()
- typename = read_type(schedule_model, current_element)
- original_typename = string_substr(typename, 4, string_len(typename))
- guaranteed_instance = False
- // Is a node, so find whether or not there are some connections that are already resolved
- Element ic
- Element oc
- String poll
- String value
- oc = allOutgoingAssociationInstances(schedule_model, current_element, "PreElement")
- while (bool_and(set_len(oc) > 0, set_len(options) < 2)):
- poll = set_pop(oc)
- if (dict_in(map, read_attribute(schedule_model, poll, "label"))):
- // This incoming link is already defined, so we just have one option: the destination of the link we matched
- value = readAssociationSource(host_model, map[read_attribute(schedule_model, poll, "label")])
- if (bool_not(set_in(options, value))):
- set_add(options, value)
- if (set_len(options) < 2):
- ic = allIncomingAssociationInstances(schedule_model, current_element, "PreElement")
- while (bool_and(set_len(ic) > 0, set_len(options) < 2)):
- poll = set_pop(ic)
- if (dict_in(map, read_attribute(schedule_model, poll, "label"))):
- // This incoming link is already defined, so we just have one option: the destination of the link we matched
- value = readAssociationDestination(host_model, map[read_attribute(schedule_model, poll, "label")])
- if (bool_not(set_in(options, value))):
- set_add(options, value)
-
- if (set_len(options) == 0):
- // Is a node and no connections, so we just pick all options
- options = allInstances(host_model, original_typename)
- guaranteed_instance = True
- elif (set_len(options) > 1):
- // Multiple "only" options, which will not work out: no options!
- return set_create()!
-
- if (is_edge(schedule_model["model"][current_element])):
- Boolean src_in
- Boolean dst_in
- // Is an edge, so check for already bound source/target
- src_label = read_attribute(schedule_model, readAssociationSource(schedule_model, current_element), "label")
- dst_label = read_attribute(schedule_model, readAssociationDestination(schedule_model, current_element), "label")
- src_in = set_in(dict_keys(map), src_label)
- dst_in = set_in(dict_keys(map), dst_label)
- guaranteed_instance = True
- if (bool_and(src_in, dst_in)):
- // Source and destination are bound
- if (set_len(options) > 0):
- options = set_overlap(options, allOutgoingAssociationInstances(host_model, map[src_label], original_typename))
- if (set_len(options) > 0):
- options = set_overlap(options, allIncomingAssociationInstances(host_model, map[dst_label], original_typename))
- elif (src_in):
- // Source is bound
- options = set_overlap(options, allOutgoingAssociationInstances(host_model, map[src_label], original_typename))
- elif (dst_in):
- // Destination is bound
- options = set_overlap(options, allIncomingAssociationInstances(host_model, map[dst_label], original_typename))
- else:
- // Neither is bound, so just get all of them
- options = options
- // Filter options further
- Element filtered_options
- String option
- filtered_options = set_create()
- Element bound
- bound = dict_values(map)
- while (set_len(options) > 0):
- option = set_pop(options)
- // Check for detecting same element twice
- if (bool_not(set_in(bound, option))):
- // Option is already present with another label, so skip this!
- if (dict_in(host_model["model"], option)):
- // Check if it conforms to the desired type
- if (bool_not(guaranteed_instance)):
- if (bool_not(is_nominal_instance(host_model, option, original_typename))):
- // Not an actual instance, so skip!
- continue!
- // Check for local (matching) constraints of element
- if (element_eq(read_attribute(schedule_model, current_element, "constraint"), read_root())):
- // No local constraints, so all is well
- set_add(filtered_options, option)
- else:
- // Check local constraints and add only if positive
- Element constraint_function
- Boolean result
- Element func
- constraint_function = read_attribute(schedule_model, current_element, "constraint")
- func = get_func_AL_model(import_node(constraint_function))
- result = func(host_model, option)
- if (result):
- set_add(filtered_options, option)
- else:
- log("ERROR in found option: not in model")
- Element attributes
- String attribute
- Element func
- Boolean result
- Element attributes_copy
- options = filtered_options
- filtered_options = set_create()
- // Check whether all attributes have a satisfied condition
- attributes_copy = dict_keys(getAttributeList(schedule_model, current_element))
- while (set_len(options) > 0):
- option = set_pop(options)
- attributes = set_copy(attributes_copy)
- result = True
- while (set_len(attributes) > 0):
- attribute = set_pop(attributes)
- if (bool_not(string_startswith(attribute, "constraint_"))):
- continue!
- value = read_attribute(schedule_model, current_element, attribute)
- // Attribute might be undefined, so skip if it is
- if (element_neq(value, read_root())):
- func = get_func_AL_model(import_node(value))
- result = func(read_attribute(host_model, option, string_substr(attribute, string_len("constraint_"), string_len(attribute) + 1)))
- else:
- result = True
- if (bool_not(result)):
- break!
- // Check value of last result, which will be True if all passed, or False otherwise
- if (result):
- set_add(filtered_options, option)
- options = filtered_options
- return options!
- Element function full_match(host_model : Element, schedule_model : Element, current : String, single_ok : Boolean):
- Element NACs
- Element NACs_backup
- String LHS
- String NAC
- Element mappings
- Element mapping
- Element result
- Element final_mappings
- Boolean allowed
- final_mappings = set_create()
- // First match the LHS part itself to get initial mappings
- LHS = set_pop(allAssociationDestinations(schedule_model, current, "LHSLink"))
- mappings = match(host_model, schedule_model, LHS, dict_create())
- // Got a list of all possible mappings, now filter based on NACs
- NACs_backup = allAssociationDestinations(schedule_model, current, "NACLink")
- // For each possible mapping, we check all NACs!
- while (set_len(mappings) > 0):
- mapping = set_pop(mappings)
- allowed = True
- NACs = set_copy(NACs_backup)
- while (set_len(NACs) > 0):
- NAC = set_pop(NACs)
- result = match(host_model, schedule_model, NAC, mapping)
- if (set_len(result) > 0):
- // NAC could be matched, and therefore is not allowed
- allowed = False
- break!
-
- if (allowed):
- set_add_node(final_mappings, mapping)
- if (single_ok):
- break!
- return final_mappings!
- Element function match(host_model : Element, schedule_model : Element, LHS : String, initial_mapping : Element):
- // Match the schedule_model to the host_model, returning all possible mappings from schedule_model elements to host_model elements
- // Make the schedule first
- Element schedule
- schedule = make_matching_schedule(schedule_model, LHS, dict_keys(initial_mapping))
- // Now follow the schedule, incrementally building all mappings
- Element mappings
- Element new_mappings
- Element new_map
- String current_element
- Element map
- String option
- Element options
- mappings = set_create()
- set_add_node(mappings, initial_mapping)
- while (list_len(schedule) > 0):
- current_element = list_pop(schedule, list_len(schedule) - 1)
- //log("Binding element with label " + cast_value(read_attribute(schedule_model, current_element, "label")))
- new_mappings = dict_create()
- while (set_len(mappings) > 0):
- map = set_pop(mappings)
- options = get_possible_bindings(host_model, schedule_model, current_element, map)
- while (set_len(options) > 0):
- option = set_pop(options)
- new_map = dict_copy(map)
- dict_add_fast(new_map, read_attribute(schedule_model, current_element, "label"), option)
- set_add_node(new_mappings, new_map)
- mappings = new_mappings
- //log("Remaining options: " + cast_value(set_len(mappings)))
- if (set_len(mappings) == 0):
- // Stop because we have no more options remaining!
- return set_create()!
- // Finished, so try the global constraint
- String constraint
- Element func
- Boolean result
- new_mappings = dict_create()
- constraint = read_attribute(schedule_model, LHS, "constraint")
- if (element_neq(constraint, read_root())):
- while (set_len(mappings) > 0):
- map = set_pop(mappings)
- func = get_func_AL_model(import_node(constraint))
- result = func(host_model, map)
- if (result):
- set_add_node(new_mappings, map)
- mappings = new_mappings
- return mappings!
- Void function rewrite(host_model : Element, schedule_model : Element, RHS : String, mapping : Element):
- // Rewrite the host model based on the mapping combined with the RHS
- Element LHS_labels
- Element RHS_labels
- Element RHS_elements
- Element remaining
- String elem
- String label
- Element labels_to_remove
- Element labels_to_add
- String typename
- String original_typename
- String src
- String dst
- Element new_mapping
- String new_name
- Element RHS_map
- String tmp
- String value
- String action
- Element original_RHS_labels
- String new_identifier
- Element func
- Element reverse
- reverse = make_reverse_dictionary(schedule_model["model"])
- LHS_labels = dict_keys(mapping)
- RHS_labels = set_create()
- RHS_map = dict_create()
- RHS_elements = allAssociationDestinations(schedule_model, RHS, "RHS_contains")
- while (set_len(RHS_elements) > 0):
- tmp = set_pop(RHS_elements)
- label = read_attribute(schedule_model, tmp, "label")
- set_add(RHS_labels, label)
- dict_add_fast(RHS_map, label, tmp)
- remaining = set_overlap(LHS_labels, RHS_labels)
- original_RHS_labels = set_copy(RHS_labels)
- while (set_len(remaining) > 0):
- elem = set_pop(remaining)
- set_remove(LHS_labels, elem)
- set_remove(RHS_labels, elem)
- labels_to_remove = LHS_labels
- labels_to_add = set_to_list(RHS_labels)
- new_mapping = dict_copy(mapping)
- while (list_len(labels_to_add) > 0):
- // Add the elements linked to these labels
- label = list_pop(labels_to_add, list_len(labels_to_add) - 1)
- // Check if we have any requirement for the ID
- if (element_eq(read_attribute(schedule_model, RHS_map[label], "__id__"), read_root())):
- new_identifier = ""
- else:
- func = get_func_AL_model(import_node(read_attribute(schedule_model, RHS_map[label], "__id__")))
- new_identifier = func(host_model, mapping)
- if (is_edge(schedule_model["model"][RHS_map[label]])):
- // Edge
- src = read_attribute(schedule_model, reverse[cast_id(read_edge_src(schedule_model["model"][RHS_map[label]]))], "label")
- dst = read_attribute(schedule_model, reverse[cast_id(read_edge_dst(schedule_model["model"][RHS_map[label]]))], "label")
- // First check whether both source and destination are already created
- if (bool_and(dict_in(new_mapping, src), dict_in(new_mapping, dst))):
- // Both are present, so we can make the link
- typename = read_type(schedule_model, RHS_map[label])
- original_typename = string_substr(typename, 5, string_len(typename))
- new_name = instantiate_link(host_model, original_typename, new_identifier, new_mapping[src], new_mapping[dst])
- dict_add_fast(new_mapping, label, new_name)
- else:
- // Delay this a bit, until all are bound
- list_insert(labels_to_add, label, 0)
- else:
- // Node
- // Create the node and add it
- typename = read_type(schedule_model, RHS_map[label])
- original_typename = string_substr(typename, 5, string_len(typename))
- new_name = instantiate_node(host_model, original_typename, new_identifier)
- dict_add_fast(new_mapping, label, new_name)
- Element attributes
- String attribute
- Element result
- while (set_len(original_RHS_labels) > 0):
- // Perform actions
- label = set_pop(original_RHS_labels)
- // Do all attribute actions that are defined
- attributes = dict_keys(getAttributeList(schedule_model, RHS_map[label]))
- while (set_len(attributes) > 0):
- attribute = set_pop(attributes)
- if (bool_not(string_startswith(attribute, "value_"))):
- continue!
- value = read_attribute(schedule_model, RHS_map[label], attribute)
- if (element_neq(value, read_root())):
- func = get_func_AL_model(import_node(value))
- result = func(host_model, new_mapping[label], mapping)
- if (element_eq(result, read_root())):
- unset_attribute(host_model, new_mapping[label], string_substr(attribute, string_len("value_"), string_len(attribute) + 1))
- elif (has_value(result)):
- // New value defined, so assign!
- instantiate_attribute(host_model, new_mapping[label], string_substr(attribute, string_len("value_"), string_len(attribute) + 1), result)
- elif (is_error(result)):
- log("Error in evaluation of attribute " + attribute)
- log("On element with label " + label)
- else:
- // Try to interpret as code
- instantiate_attribute_code(host_model, new_mapping[label], string_substr(attribute, string_len("value_"), string_len(attribute) + 1), result)
- // Do the global action of each element
- action = read_attribute(schedule_model, RHS_map[label], "action")
- if (element_neq(action, read_root())):
- Element func
- func = get_func_AL_model(import_node(action))
- func(host_model, new_mapping[label], mapping)
- while (set_len(labels_to_remove) > 0):
- // Remove the elements linked to these labels
- label = set_pop(labels_to_remove)
- model_delete_element(host_model, mapping[label])
- dict_delete(new_mapping, label)
- // Execute global action (whatever it may be)
- action = read_attribute(schedule_model, RHS, "action")
- if (element_neq(action, read_root())):
- Element func
- func = get_func_AL_model(import_node(action))
- func(host_model, new_mapping)
- return!
- Element function transform(host_model : Element, schedule_model : Element):
- // Find initial model
- Element all_composites
- String composite
- String current
- Element merged
- Element original_mm
- // Now start transforming for real
- all_composites = allInstances(schedule_model, "Composite")
- if (set_len(all_composites) == 1):
- // Only one, so it is easy
- current = set_pop(all_composites)
- else:
- // Filter out those that are themselves contained
- while (set_len(all_composites) > 0):
- composite = set_pop(all_composites)
- if (set_len(allIncomingAssociationInstances(schedule_model, composite, "Contains")) == 0):
- // Isn't contained in any, so this is the root model!
- current = composite
- if (transform_composite(host_model, schedule_model, current)):
- // Success, so return True if it is in-place, or the new model if it is out-place
- return True!
- else:
- return False!
- Boolean function transform_composite(host_model : Element, schedule_model : Element, composite : String):
- String current
- String typename
- Boolean result
- current = set_pop(allAssociationDestinations(schedule_model, composite, "Initial"))
- while (is_nominal_instance(schedule_model, current, "Rule")):
- // Still a rule that we must execute
- typename = read_type(schedule_model, current)
- if (typename == "Atomic"):
- result = transform_atomic(host_model, schedule_model, current)
- elif (typename == "Query"):
- result = transform_query(host_model, schedule_model, current)
- elif (typename == "Composite"):
- result = transform_composite(host_model, schedule_model, current)
- elif (typename == "ForAll"):
- result = transform_forall(host_model, schedule_model, current)
- Element result_set
- if (result):
- result_set = allAssociationDestinations(schedule_model, current, "OnSuccess")
- else:
- result_set = allAssociationDestinations(schedule_model, current, "OnFailure")
- if (set_len(result_set) == 0):
- log("ERROR: no next rule found for execution result " + cast_string(result))
- elif (set_len(result_set) > 1):
- log("WARNING: multiple next rules found for execution result " + cast_string(result))
- log("Picking one at random...")
- current = set_pop(result_set)
- // No longer a rule, so it is either success or failure
- if (is_nominal_instance(schedule_model, current, "Success")):
- return True!
- else:
- return False!
- Boolean function transform_atomic(host_model : Element, schedule_model : Element, current : String):
- // Execute the atomic transformation
- Element mappings
- Element mapping
- mappings = full_match(host_model, schedule_model, current, True)
- //log("Executing rule: " + current)
- if (set_len(mappings) > 0):
- // Pick one!
- mapping = random_choice(set_to_list(mappings))
- String RHS
- RHS = set_pop(allAssociationDestinations(schedule_model, current, "RHSLink"))
- rewrite(host_model, schedule_model, RHS, mapping)
- //log("Rewrite OK")
- return True!
- else:
- return False!
- Boolean function transform_forall(host_model : Element, schedule_model : Element, current : String):
- // Execute the atomic transformation
- Element mappings
- String RHS
- Element mapping
- Boolean result
- //log("Executing rule: " + current)
- mappings = full_match(host_model, schedule_model, current, False)
- if (set_len(mappings) > 0):
- result = True
- else:
- result = False
- //log("Matches in forall: " + cast_string(set_len(mappings)))
- while (set_len(mappings) > 0):
- mapping = set_pop(mappings)
- RHS = set_pop(allAssociationDestinations(schedule_model, current, "RHSLink"))
- rewrite(host_model, schedule_model, RHS, mapping)
- //log("Rewrite OK")
- return result!
- Boolean function transform_query(host_model : Element, schedule_model : Element, current : String):
- // Execute the transformation
- Element mappings
- Element mapping
- mappings = full_match(host_model, schedule_model, current, True)
- if (set_len(mappings) > 0):
- return True!
- else:
- return False!
|