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 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) 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_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, "") dict_add_fast(new_mapping, label, new_name) Element attributes String attribute Element result Element func 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) 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) 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) 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!