123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352 |
- import re
- from wrappers import modelverse as mv
- class Edge(object):
- """
- Small helper class for association validation.
- Represents an edge as a connection between two nodes (n1, n2).
- Does not assume any direction. As such, the eq and hash implementations
- do not care about the ordering and an Edge("a", "b") object is equal to Edge("b", "a").
- """
- def __init__(self, n1, n2):
- self.n1 = n1
- self.n2 = n2
- def __eq__(self, other):
- # type: (Edge) -> bool
- if other.n1 == self.n1 or other.n1 == self.n2 and other.n2 == self.n1 or other.n2 == self.n2:
- return True
- return False
- def __hash__(self):
- return hash(self.n1) ^ hash(self.n2)
- def __repr__(self):
- return "{}-{}".format(self.n1, self.n2)
- class Attribute(object):
- """
- Helper class for attribute representation as key value pair
- """
- def __init__(self, key, val):
- self.key = key
- self.val = val
- def __eq__(self, other):
- if other.key == self.key and other.val == self.val:
- return True
- return False
- def __hash__(self):
- return hash(self.key + self.val)
- def __repr__(self):
- return "{}={}".format(self.key, self.val)
- def all_models():
- """ Returns a list of paths of all example- and instance models """
- return all_instance_models() + all_example_models()
- def all_instance_models():
- """ Returns a list of paths of all instance models """
- try:
- instance_models = mv.model_list("models/instance")
- except mv.UnknownLocation:
- # no instance models
- return []
- instance_models_full = ["models/instance/"+m for m in instance_models]
- return instance_models_full
- def all_example_models():
- """ Returns a list of paths of all example models """
- try:
- example_models = mv.model_list("models/example")
- except mv.UnknownLocation:
- # no example models
- return []
- example_models_full = ["models/example/"+exm for exm in example_models]
- return example_models_full
- def all_consyn_models():
- """ Returns a list of paths of all concrete syntax models """
- try:
- consyn_models = mv.model_list("models/consyn")
- except mv.UnknownLocation:
- return []
- consyn_models_full = ["models/consyn/"+csm for csm in consyn_models]
- return consyn_models_full
- def is_example_model(model):
- """
- Returns true if model is a example model, false otherwise.
- """
- model_id = mv.all_instances(model, "Model").pop()
- is_example_str = mv.read_attrs(model, model_id)["is_example"]
- return is_example_str == u"True"
- def is_instance_model(model):
- """
- Returns true if model is an instance model, false otherwise.
- """
- is_example = is_example_model(model)
- return not is_example
- def all_nodes_with_type(model, typ):
- """ Returns a list of nodes in model model with type typ """
- all_nodes = mv.all_instances(model, "Node")
- ret = [node for node in all_nodes if mv.read_attrs(model, node)["typeID"] == typ]
- return ret
- def get_node_type(model, node_id):
- """ Returns the type attribute of node in model as string"""
- return mv.read_attrs(model, node_id)["typeID"]
- def get_available_types():
- """ Returns a list of all types of nodes in all example models """
- types = []
- for m in all_example_models():
- nodes = mv.all_instances(m, "Node")
- for node in nodes:
- typ = get_node_type(m, node)
- types.append(typ)
- return list(set(types))
- def count_occurences(node_type, model):
- """ Returns the number of occurences of a node with type node_type in a model """
- ctr = 0
- all_nodes = mv.all_instances(model, "Node")
- for node in all_nodes:
- typ = get_node_type(model, node)
- if typ == node_type:
- ctr += 1
- return ctr
- def is_type_mandatory(node_type):
- """
- True if node_type is a mandatory type (i.e. it occurs at least once in every example model),
- False otherwise.
- """
- for exm in all_example_models():
- nodes_with_type = all_nodes_with_type(exm, node_type)
- if not nodes_with_type:
- return False
- return True
- def get_associations_between(model, node1, node2):
- """ Returns a list of association IDs between the nodes node1 and node2 """
- edges_n1 = mv.read_outgoing(model, node1, "Edge")
- edges_n1.update(mv.read_incoming(model, node1, "Edge"))
- edges_n2 = mv.read_outgoing(model, node2, "Edge")
- edges_n2.update(mv.read_incoming(model, node2, "Edge"))
- return list(edges_n1.intersection(edges_n2))
- def get_all_attributes_of_type(model, node_type):
- # type: (str, str) -> list(Attribute)
- """ Returns a list of attributes of a node with type node_type in a model """
- ret = []
- node_attribute_links = mv.all_instances(model, "NodeAttribute")
- for link in node_attribute_links:
- src_node = mv.read_association_source(model, link)[0]
- src_typ = mv.read_attrs(model, src_node)["typeID"]
- if src_typ != node_type:
- continue
- attr_node_name = mv.read_association_destination(model, link)[0]
- attr_dict = mv.read_attrs(model, attr_node_name)
- attr = Attribute(attr_dict["key"], attr_dict["value"])
- ret.append(attr)
- return ret
- def get_attributes_of_node(model, node_id):
- # type: (str, str) -> list(Attribute)
- """ Returns a list of attributes of a specific node with id node_id """
- ret = []
- outgoings = mv.read_outgoing(model, node_id, "NodeAttribute")
- if not outgoings:
- return []
- for link in outgoings:
- dest = mv.read_association_destination(model, link)[0]
- attr_dict = mv.read_attrs(model, dest)
- attr = Attribute(attr_dict["key"], attr_dict["value"])
- ret.append(attr)
- return ret
- def model_contains_type(model, node_type):
- """
- True if some node in model is typed by node_type, False otherwise.
- """
- all_types = all_nodes_with_type(model, node_type)
- if not all_types:
- return False
- return True
- def has_edge_to_type(model, node_id, node_type):
- """
- True if the node identified by node_id is connected to a node of type type_id, False otherwise.
- """
- outgoings = mv.read_outgoing(model, node_id, "Edge")
- dest_ids = [mv.read_association_destination(model, edge)[0] for edge in outgoings]
- dest_types = [get_node_type(model, _node_id) for _node_id in dest_ids]
- # also need to check incomings of node_id
- incomings = mv.read_incoming(model, node_id, "Edge")
- src_ids = [mv.read_association_source(model, edge)[0] for edge in incomings]
- src_types = [get_node_type(model, _node_id) for _node_id in src_ids]
- return node_type in dest_types + src_types
- def is_edge_supported(from_type, to_type):
- """
- True if an association between from_type and to_type exists in any example model
- False otherwise
- """
- for m in all_example_models():
- nodes_from = all_nodes_with_type(m, from_type)
- nodes_to = all_nodes_with_type(m, to_type)
- for nf in nodes_from:
- for nt in nodes_to:
- assocs = get_associations_between(m, nf, nt)
- if assocs:
- # somewhere in an example model, there is such an association
- return True
- return False
- def new_instance_model():
- """
- Adds a new, empty instance model to the Modelverse.
- Returns the name of the new model
- """
- existing_models = all_instance_models()
- idx = 1
- nums = []
- for model in existing_models:
- m = model.split("/")[-1]
- try:
- idx = int(re.search(r'\d+', m).group())
- nums.append(idx)
- except AttributeError:
- pass
- if nums:
- idx = sorted(nums)[-1] + 1
- im = "models/instance/im{}".format(idx)
- print("Adding new instance model {}".format(im))
- mv.model_add(im, "formalisms/graphMM")
- mid = mv.instantiate(im, "Model")
- mv.attr_assign(im, mid, "is_example", False)
- mv.attr_assign(im, mid, "descr", "")
- return im
- def new_example_model():
- """
- Adds a new, empty example model to the Modelverse.
- Returns the name of the new model
- """
- existing_models = all_example_models()
- idx = 1
- nums = []
- for model in existing_models:
- m = model.split("/")[-1]
- try:
- idx = int(re.search(r'\d+', m).group())
- nums.append(idx)
- except AttributeError:
- pass
- if nums:
- idx = sorted(nums)[-1] + 1
- exm = "models/example/ex{}".format(idx)
- print("Adding new example model {}".format(exm))
- mv.model_add(exm, "formalisms/graphMM")
- mid = mv.instantiate(exm, "Model")
- mv.attr_assign(exm, mid, "is_example", True)
- mv.attr_assign(exm, mid, "descr", "")
- return exm
- def get_nodes_with_attribute(model, attr_key, node_type):
- """
- Returns all nodes which have an attribute identified by attr_key and are typed by node_type
- """
- ret = []
- typed_nodes = all_nodes_with_type(model, node_type)
- for node_id in typed_nodes:
- attrs = get_attributes_of_node(model, node_id)
- for attr in attrs:
- if attr.key == attr_key:
- ret.append(node_id)
- return ret
- def has_attribute_key(model, node_id, attr_key):
- """
- Helper function: True if node with node_id has an attribute with key attr_key, False otherwise.
- """
- attrs = get_attributes_of_node(model, node_id)
- for attr in attrs:
- if attr.key == attr_key:
- return True
- return False
- def is_attribute_mandatory(node_type, attr_key):
- """
- Helper for attribute check that returns True if the attribute attr_key of type node_type is mandatory by
- looking at all example models.
- """
- # iterate through all example models: If every node with type node_type has the attribute with attr_key,
- # it is mandatory
- for exm in all_example_models():
- nodes_of_type = all_nodes_with_type(exm, node_type)
- for node in nodes_of_type:
- if not has_attribute_key(exm, node, attr_key):
- return False
- return True
- def is_attribute_valid(node_type, attr_key):
- """
- Check if attributing with key is allowed for a node of type node_type.
- Goes through all example models and checks if they have such an attributes.
- """
- for exm in all_example_models():
- all_attrs = get_all_attributes_of_type(exm, node_type)
- if attr_key in [attr.key for attr in all_attrs]:
- return True
- return False
- def get_mandtory_edges():
- """
- Returns a list of mandatory edges from all example models.
- """
- example_models = all_example_models()
- all_edges = set()
- for exm in example_models:
- all_links = mv.all_instances(exm, "Edge")
- for link in all_links:
- src_id = mv.read_association_source(exm, link)[0]
- dest_id = mv.read_association_destination(exm, link)[0]
- src_type = get_node_type(exm, src_id)
- dest_type = get_node_type(exm, dest_id)
- all_edges.add(Edge(src_type, dest_type))
- edge_mandatory = {e: True for e in all_edges}
- for cand_edge, _ in edge_mandatory.iteritems():
- # check every example model if it contains the required types
- # and if there are two noes of the type which are not connected -> not mandatory
- found = False
- for exm in example_models:
- if found:
- break
- typed_nodes_a = all_nodes_with_type(exm, cand_edge.n1)
- typed_nodes_b = all_nodes_with_type(exm, cand_edge.n2)
- if not typed_nodes_a or not typed_nodes_b:
- # example model does not contain the two types
- continue
- for src_node in typed_nodes_a:
- # if this node is not connected to a node typed by the required type, the edge is not mandatory
- if not has_edge_to_type(exm, src_node, cand_edge.n2):
- edge_mandatory[cand_edge] = False
- found = True
- mandatory_edges = [edge for edge, mandatory in edge_mandatory.iteritems() if mandatory]
- return mandatory_edges
|