|
@@ -5,19 +5,25 @@ import commons
|
|
|
class Edge(object):
|
|
|
"""
|
|
|
Small helper class for association validation.
|
|
|
- Stores an edge as a tuple of (source, dest)
|
|
|
+ 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, src, dest):
|
|
|
- self.src = src
|
|
|
- self.dest = dest
|
|
|
+ def __init__(self, n1, n2):
|
|
|
+ self.n1 = n1
|
|
|
+ self.n2 = n2
|
|
|
|
|
|
def __eq__(self, other):
|
|
|
- if other.src == self.src and other.dest == self.dest:
|
|
|
+ # 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.src, self.dest)
|
|
|
+ return "{}-{}".format(self.n1, self.n2)
|
|
|
|
|
|
|
|
|
class Verifier(object):
|
|
@@ -118,6 +124,7 @@ class Verifier(object):
|
|
|
attribute key in some example model.
|
|
|
2. An attribute for a type is mandatory if it occurs in every example model.
|
|
|
"""
|
|
|
+
|
|
|
# Check attribute keys for every node in instance model
|
|
|
all_nodes = mv.all_instances(self._instance_model, "Node")
|
|
|
for node in all_nodes:
|
|
@@ -172,135 +179,37 @@ class Verifier(object):
|
|
|
to connect two nodes).
|
|
|
"""
|
|
|
|
|
|
+ # get a set of mandatory edges (= edges that occur in every example model)
|
|
|
+ all_edges = {model: set() for model in self._example_models}
|
|
|
+ for exm in self._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 = commons.get_node_type(exm, src_id)
|
|
|
+ dest_type = commons.get_node_type(exm, dest_id)
|
|
|
+ all_edges[exm].add(Edge(src_type, dest_type))
|
|
|
+ mandatory_edges = set.intersection(*all_edges.values())
|
|
|
+
|
|
|
+ # check if instance model contains the types and the edge
|
|
|
+ for edge in mandatory_edges:
|
|
|
+ if not commons.model_contains_type(self._instance_model, edge.n1):
|
|
|
+ continue
|
|
|
+ if not commons.model_contains_type(self._instance_model, edge.n2):
|
|
|
+ continue
|
|
|
+ # instance model contains both types
|
|
|
+ for node in commons.all_nodes_with_type(self._instance_model, edge.n1):
|
|
|
+ if not commons.has_edge_to_type(self._instance_model, node, edge.n2):
|
|
|
+ return {"OK":False, "error": "Edge between {} and {} mandatory".format(edge.n1, edge.n2)}
|
|
|
+
|
|
|
+ # lastly, check if all edges in the instance model are actually supported
|
|
|
+ all_edges = mv.all_instances(self._instance_model, "Edge")
|
|
|
+ for edge in all_edges:
|
|
|
+ src_id = mv.read_association_source(self._instance_model, edge)[0]
|
|
|
+ dest_id = mv.read_association_destination(self._instance_model, edge)[0]
|
|
|
+ src_type = commons.get_node_type(self._instance_model, src_id)
|
|
|
+ dest_type = commons.get_node_type(self._instance_model, dest_id)
|
|
|
+ if not commons.is_edge_supported(src_type, dest_type):
|
|
|
+ return {"OK":False, "error": "Edge between {} and {} not supported".format(edge.n1, edge.n2)}
|
|
|
|
|
|
return {"OK": True, "error": None}
|
|
|
-
|
|
|
-def verify(instance_model_path):
|
|
|
- """
|
|
|
- Verify an instance model against all example models (mv folder currently hard-coded)
|
|
|
- Performs checks similar to the linguistic conformance check as seen in the MDE class:
|
|
|
- 1. Node typing
|
|
|
- 2. Node cardinality
|
|
|
- 3. Attribute validity
|
|
|
- 4. Associations
|
|
|
- """
|
|
|
- example_models = commons.all_example_models()
|
|
|
-
|
|
|
- # 1. Check if all nodes in instance model are typed by a node in the example models
|
|
|
- # For that, first get all available types from example models
|
|
|
- print("Verify: checking node typing ...")
|
|
|
- all_types = []
|
|
|
- for exm in example_models:
|
|
|
- all_nodes = mv.all_instances(exm, "Node")
|
|
|
- for node in all_nodes:
|
|
|
- typ = mv.read_attrs(exm, node)["typeID"]
|
|
|
- if not typ in all_types:
|
|
|
- all_types.append(typ)
|
|
|
- #print("Verify: types found in example models: {}".format(all_types))
|
|
|
-
|
|
|
- # Now, check for every element in instance model if it is a valid type
|
|
|
- all_nodes = mv.all_instances(instance_model_path, "Node")
|
|
|
- for node in all_nodes:
|
|
|
- node_typ = mv.read_attrs(instance_model_path, node)["typeID"]
|
|
|
- if node_typ not in all_types:
|
|
|
- return {"OK":False, "error":"Type {} from instance model not in example models".format(node_typ)}
|
|
|
-
|
|
|
- # 2. Check node cardinality: a node is mandatory in the instance model if it occurs in every
|
|
|
- # example model. Additionally, it needs to occur at least n times if it occurs at least n times in every example model.
|
|
|
- print("Verify: checking node multiplicity ...")
|
|
|
- total_occurences = {k:[] for k in all_types}
|
|
|
- for node, occ_list in total_occurences.iteritems():
|
|
|
- for exm in example_models:
|
|
|
- num_occ = _count_occurences(node, exm)
|
|
|
- occ_list.append(num_occ)
|
|
|
-
|
|
|
- elem_cardinality = {k:min(v) for k, v in total_occurences.iteritems()}
|
|
|
- for el, min_occ in elem_cardinality.iteritems():
|
|
|
- if not _count_occurences(el, instance_model_path) >= min_occ:
|
|
|
- return {"OK":False, "error":"Node with type {} needs to occur at least {} times".format(el, min_occ)}
|
|
|
-
|
|
|
- # 3. Check attribute types: Is every attribute in instance model present in some example model?
|
|
|
- print("Verify: checking attributes ...")
|
|
|
- all_nodes = mv.all_instances(instance_model_path, "Node")
|
|
|
- for node in all_nodes:
|
|
|
- node_typ = mv.read_attrs(instance_model_path, node)["typeID"]
|
|
|
- attrs = commons.get_all_attributes_of_type(instance_model_path, node_typ)
|
|
|
- if len(attrs) == 0:
|
|
|
- continue
|
|
|
- #print("Attributes of node {} of type {} are: {}".format(node, node_typ, attrs))
|
|
|
- # check example models for such an attribute key
|
|
|
- for im_attribute in attrs:
|
|
|
- found = False
|
|
|
- for exm in example_models:
|
|
|
- if found:
|
|
|
- break
|
|
|
- exm_attrs = commons.get_all_attributes_of_type(exm, node_typ)
|
|
|
- for exm_attr in exm_attrs:
|
|
|
- if exm_attr.key == im_attribute.key:
|
|
|
- #print("Found attribute {} in model {}".format(im_attribute, exm))
|
|
|
- found = True
|
|
|
- break
|
|
|
- if not found:
|
|
|
- return {"OK":False, "error":"Attribute {} not found in example models".format(im_attribute)}
|
|
|
-
|
|
|
- # TODO: Attribute mandatory if present in all example model nodes
|
|
|
-
|
|
|
-
|
|
|
- # 4. Associations
|
|
|
- # Check multiplicity: an association needs to occur n>0 times if it occurs n>0 times in every example model
|
|
|
- # Check assocation type validity as well
|
|
|
- print("Verify: checking associations ...")
|
|
|
- all_edges = {k:[] for k in example_models}
|
|
|
- for exm in example_models:
|
|
|
- all_nodes = mv.all_instances(exm, "Node")
|
|
|
- for node in all_nodes:
|
|
|
- node_typ = mv.read_attrs(exm, node)["typeID"]
|
|
|
- out_associations = mv.read_outgoing(exm, node, "Edge")
|
|
|
- for assoc in out_associations:
|
|
|
- if assoc.startswith("__"):
|
|
|
- continue
|
|
|
- target = mv.read_association_destination(exm, assoc)[0]
|
|
|
- target_typ = mv.read_attrs(exm, target)["typeID"]
|
|
|
- all_edges[exm].append(Edge(node_typ, target_typ))
|
|
|
-
|
|
|
- unique_edges = []
|
|
|
- for edge_lst in all_edges.values():
|
|
|
- for edge in edge_lst:
|
|
|
- if not edge in unique_edges:
|
|
|
- unique_edges.append(edge)
|
|
|
-
|
|
|
- edge_ctr = {k:[] for k in unique_edges}
|
|
|
- for edge, ctr_lst in edge_ctr.iteritems():
|
|
|
- for exm, edges in all_edges.iteritems():
|
|
|
- ctr_lst.append(edges.count(edge))
|
|
|
-
|
|
|
- for k, v in edge_ctr.iteritems():
|
|
|
- edge_ctr[k] = min(v)
|
|
|
-
|
|
|
- print(edge_ctr) # {Edge("AP", "Tablet"): 0, Edge("Router", "PC"): 1, ...}
|
|
|
- return {"OK": True, "error": None}
|
|
|
-
|
|
|
- # check if mandatory edges are present in instance model
|
|
|
- # first, get all edges in instance model
|
|
|
- all_edges = mv.all_instances(instance_model_path, "Edge")
|
|
|
- im_edges = []
|
|
|
- for edge in all_edges:
|
|
|
- if edge.startswith("__"):
|
|
|
- continue
|
|
|
- edge_src = mv.read_association_source(instance_model_path, edge)[0]
|
|
|
- edge_dest = mv.read_association_destination(instance_model_path, edge)[0]
|
|
|
- edge_src_typ = mv.read_attrs(instance_model_path, edge_src)["typeID"]
|
|
|
- edge_dest_typ = mv.read_attrs(instance_model_path, edge_dest)["typeID"]
|
|
|
- im_edges.append(Edge(edge_src_typ, edge_dest_typ))
|
|
|
-
|
|
|
- # Check if all edges in instance models are valid (connecting types correspond to example models)
|
|
|
- for edge in im_edges:
|
|
|
- if edge not in edge_ctr.keys():
|
|
|
- return {"OK":False, "error":"Edge {} not valid according to example models".format(edge)}
|
|
|
-
|
|
|
- # now, check if for every mandatory edge, it occurs sufficiently enough
|
|
|
- for edge, cnt in edge_ctr.iteritems():
|
|
|
- if im_edges.count(edge) < cnt:
|
|
|
- return {"OK":False, "error":"Edge {} needs to appear {} times in instance model".format(edge, cnt)}
|
|
|
-
|
|
|
- return {"OK":True, "error":None}
|