commons.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. import re
  2. from wrappers import modelverse as mv
  3. class Edge(object):
  4. """
  5. Small helper class for association validation.
  6. Represents an edge as a connection between two nodes (n1, n2).
  7. Does not assume any direction. As such, the eq and hash implementations
  8. do not care about the ordering and an Edge("a", "b") object is equal to Edge("b", "a").
  9. """
  10. def __init__(self, n1, n2):
  11. self.n1 = n1
  12. self.n2 = n2
  13. def __eq__(self, other):
  14. # type: (Edge) -> bool
  15. if other.n1 == self.n1 or other.n1 == self.n2 and other.n2 == self.n1 or other.n2 == self.n2:
  16. return True
  17. return False
  18. def __hash__(self):
  19. return hash(self.n1) ^ hash(self.n2)
  20. def __repr__(self):
  21. return "{}-{}".format(self.n1, self.n2)
  22. class Attribute(object):
  23. """
  24. Helper class for attribute representation as key value pair
  25. """
  26. def __init__(self, key, val):
  27. self.key = key
  28. self.val = val
  29. def __eq__(self, other):
  30. if other.key == self.key and other.val == self.val:
  31. return True
  32. return False
  33. def __repr__(self):
  34. return "{}={}".format(self.key, self.val)
  35. def all_models():
  36. """ Returns a list of paths of all example- and instance models """
  37. return all_instance_models() + all_example_models()
  38. def all_instance_models():
  39. """ Returns a list of paths of all instance models """
  40. try:
  41. instance_models = mv.model_list("models/instance")
  42. except mv.UnknownLocation:
  43. # no instance models
  44. return []
  45. instance_models_full = ["models/instance/"+m for m in instance_models]
  46. return instance_models_full
  47. def all_example_models():
  48. """ Returns a list of paths of all example models """
  49. try:
  50. example_models = mv.model_list("models/example")
  51. except mv.UnknownLocation:
  52. # no example models
  53. return []
  54. example_models_full = ["models/example/"+exm for exm in example_models]
  55. return example_models_full
  56. def all_consyn_models():
  57. """ Returns a list of paths of all concrete syntax models """
  58. try:
  59. consyn_models = mv.model_list("models/consyn")
  60. except mv.UnknownLocation:
  61. return []
  62. consyn_models_full = ["models/consyn/"+csm for csm in consyn_models]
  63. return consyn_models_full
  64. def all_nodes_with_type(model, typ):
  65. """ Returns a list of nodes in model model with type typ """
  66. all_nodes = mv.all_instances(model, "Node")
  67. ret = [node for node in all_nodes if mv.read_attrs(model, node)["typeID"] == typ]
  68. return ret
  69. def get_node_type(model, node_id):
  70. """ Returns the type attribute of node in model as string"""
  71. return mv.read_attrs(model, node_id)["typeID"]
  72. def get_available_types():
  73. """ Returns a list of all types of nodes in all example models """
  74. types = []
  75. for m in all_example_models():
  76. nodes = mv.all_instances(m, "Node")
  77. for node in nodes:
  78. typ = get_node_type(m, node)
  79. types.append(typ)
  80. return list(set(types))
  81. def count_occurences(node_type, model):
  82. """ Returns the number of occurences of a node with type node_type in a model """
  83. ctr = 0
  84. all_nodes = mv.all_instances(model, "Node")
  85. for node in all_nodes:
  86. typ = get_node_type(model, node)
  87. if typ == node_type:
  88. ctr += 1
  89. return ctr
  90. def is_type_mandatory(node_type):
  91. """
  92. True if node_type is a mandatory type (i.e. it occurs at least once in every example model),
  93. False otherwise.
  94. """
  95. for exm in all_example_models():
  96. nodes_with_type = all_nodes_with_type(exm, node_type)
  97. if not nodes_with_type:
  98. return False
  99. return True
  100. def get_associations_between(model, node1, node2):
  101. """ Returns a list of association IDs between the nodes node1 and node2 """
  102. edges_n1 = mv.read_outgoing(model, node1, "Edge")
  103. edges_n1.update(mv.read_incoming(model, node1, "Edge"))
  104. edges_n2 = mv.read_outgoing(model, node2, "Edge")
  105. edges_n2.update(mv.read_incoming(model, node2, "Edge"))
  106. return list(edges_n1.intersection(edges_n2))
  107. def get_all_attributes_of_type(model, node_type):
  108. # type: (str, str) -> list(Attribute)
  109. """ Returns a list of attributes of a node with type node_type in a model """
  110. ret = []
  111. node_attribute_links = mv.all_instances(model, "NodeAttribute")
  112. for link in node_attribute_links:
  113. src_node = mv.read_association_source(model, link)[0]
  114. src_typ = mv.read_attrs(model, src_node)["typeID"]
  115. if src_typ != node_type:
  116. continue
  117. attr_node_name = mv.read_association_destination(model, link)[0]
  118. attr_dict = mv.read_attrs(model, attr_node_name)
  119. attr = Attribute(attr_dict["key"], attr_dict["value"])
  120. ret.append(attr)
  121. return ret
  122. def get_attributes_of_node(model, node_id):
  123. # type: (str, str) -> list(Attribute)
  124. """ Returns a list of attributes of a specific node with id node_id """
  125. ret = []
  126. outgoings = mv.read_outgoing(model, node_id, "NodeAttribute")
  127. if not outgoings:
  128. return []
  129. for link in outgoings:
  130. dest = mv.read_association_destination(model, link)[0]
  131. attr_dict = mv.read_attrs(model, dest)
  132. attr = Attribute(attr_dict["key"], attr_dict["value"])
  133. ret.append(attr)
  134. return ret
  135. def model_contains_type(model, node_type):
  136. """
  137. True if some node in model is typed by node_type, False otherwise.
  138. """
  139. all_types = all_nodes_with_type(model, node_type)
  140. if not all_types:
  141. return False
  142. return True
  143. def has_edge_to_type(model, node_id, node_type):
  144. """
  145. True if the node identified by node_id is connected to a node of type type_id, False otherwise.
  146. """
  147. outgoings = mv.read_outgoing(model, node_id, "Edge")
  148. dest_ids = [mv.read_association_destination(model, edge)[0] for edge in outgoings]
  149. dest_types = [get_node_type(model, _node_id) for _node_id in dest_ids]
  150. # also need to check incomings of node_id
  151. incomings = mv.read_incoming(model, node_id, "Edge")
  152. src_ids = [mv.read_association_source(model, edge)[0] for edge in incomings]
  153. src_types = [get_node_type(model, _node_id) for _node_id in src_ids]
  154. return node_type in dest_types + src_types
  155. def is_edge_supported(from_type, to_type):
  156. """
  157. True if an association between from_type and to_type exists in any example model
  158. False otherwise
  159. """
  160. for m in all_example_models():
  161. nodes_from = all_nodes_with_type(m, from_type)
  162. nodes_to = all_nodes_with_type(m, to_type)
  163. for nf in nodes_from:
  164. for nt in nodes_to:
  165. assocs = get_associations_between(m, nf, nt)
  166. if assocs:
  167. # somewhere in an example model, there is such an association
  168. return True
  169. return False
  170. def new_instance_model():
  171. """
  172. Adds a new, empty instance model to the Modelverse.
  173. Returns the name of the new model
  174. """
  175. existing_models = all_instance_models()
  176. idx = 1
  177. nums = []
  178. for model in existing_models:
  179. m = model.split("/")[-1]
  180. try:
  181. idx = int(re.search(r'\d+', m).group())
  182. nums.append(idx)
  183. except AttributeError:
  184. pass
  185. if nums:
  186. idx = sorted(nums)[-1] + 1
  187. im = "models/instance/im{}".format(idx)
  188. print("Adding new instance model {}".format(im))
  189. mv.model_add(im, "formalisms/graphMM")
  190. mid = mv.instantiate(im, "Model")
  191. mv.attr_assign(im, mid, "is_example", False)
  192. mv.attr_assign(im, mid, "descr", "")
  193. return im
  194. def new_example_model():
  195. """
  196. Adds a new, empty example model to the Modelverse.
  197. Returns the name of the new model
  198. """
  199. existing_models = all_example_models()
  200. idx = 1
  201. nums = []
  202. for model in existing_models:
  203. m = model.split("/")[-1]
  204. try:
  205. idx = int(re.search(r'\d+', m).group())
  206. nums.append(idx)
  207. except AttributeError:
  208. pass
  209. if nums:
  210. idx = sorted(nums)[-1] + 1
  211. exm = "models/example/ex{}".format(idx)
  212. print("Adding new example model {}".format(exm))
  213. mv.model_add(exm, "formalisms/graphMM")
  214. mid = mv.instantiate(exm, "Model")
  215. mv.attr_assign(exm, mid, "is_example", True)
  216. mv.attr_assign(exm, mid, "descr", "")
  217. return exm
  218. def get_nodes_with_attribute(model, attr_key, node_type):
  219. """
  220. Returns all nodes which have an attribute identified by attr_key and are typed by node_type
  221. """
  222. ret = []
  223. typed_nodes = all_nodes_with_type(model, node_type)
  224. for node_id in typed_nodes:
  225. attrs = get_attributes_of_node(model, node_id)
  226. for attr in attrs:
  227. if attr.key == attr_key:
  228. ret.append(node_id)
  229. return ret
  230. def has_attribute_key(model, node_id, attr_key):
  231. """
  232. Helper function: True if node with node_id has an attribute with key attr_key, False otherwise.
  233. """
  234. attrs = get_attributes_of_node(model, node_id)
  235. for attr in attrs:
  236. if attr.key == attr_key:
  237. return True
  238. return False
  239. def is_attribute_mandatory(node_type, attr_key):
  240. """
  241. Helper for attribute check that returns True if the attribute attr_key of type node_type is mandatory by
  242. looking at all example models.
  243. """
  244. # iterate through all example models: If every node with type node_type has the attribute with attr_key,
  245. # it is mandatory
  246. for exm in all_example_models():
  247. nodes_of_type = all_nodes_with_type(exm, node_type)
  248. for node in nodes_of_type:
  249. if not has_attribute_key(exm, node, attr_key):
  250. return False
  251. return True
  252. def is_attribute_valid(node_type, attr_key):
  253. """
  254. Check if attributing with key is allowed for a node of type node_type.
  255. Goes through all example models and checks if they have such an attributes.
  256. """
  257. for exm in all_example_models():
  258. all_attrs = get_all_attributes_of_type(exm, node_type)
  259. if attr_key in [attr.key for attr in all_attrs]:
  260. return True
  261. return False
  262. def get_mandtory_edges():
  263. """
  264. Returns a list of mandatory edges from all example models.
  265. """
  266. example_models = all_example_models()
  267. all_edges = set()
  268. for exm in example_models:
  269. all_links = mv.all_instances(exm, "Edge")
  270. for link in all_links:
  271. src_id = mv.read_association_source(exm, link)[0]
  272. dest_id = mv.read_association_destination(exm, link)[0]
  273. src_type = get_node_type(exm, src_id)
  274. dest_type = get_node_type(exm, dest_id)
  275. all_edges.add(Edge(src_type, dest_type))
  276. edge_mandatory = {e: True for e in all_edges}
  277. for cand_edge, _ in edge_mandatory.iteritems():
  278. # check every example model if it contains the required types
  279. # and if there are two noes of the type which are not connected -> not mandatory
  280. found = False
  281. for exm in example_models:
  282. if found:
  283. break
  284. typed_nodes_a = all_nodes_with_type(exm, cand_edge.n1)
  285. typed_nodes_b = all_nodes_with_type(exm, cand_edge.n2)
  286. if not typed_nodes_a or not typed_nodes_b:
  287. # example model does not contain the two types
  288. continue
  289. for src_node in typed_nodes_a:
  290. # if this node is not connected to a node typed by the required type, the edge is not mandatory
  291. if not has_edge_to_type(exm, src_node, cand_edge.n2):
  292. edge_mandatory[cand_edge] = False
  293. found = True
  294. mandatory_edges = [edge for edge, mandatory in edge_mandatory.iteritems() if mandatory]
  295. return mandatory_edges