commons.py 12 KB

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