messages.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. '''This file is part of AToMPM - A Tool for Multi-Paradigm Modelling
  2. Copyright 2011 by the AToMPM team and licensed under the LGPL
  3. See COPYING.lesser and README.md in the root of this project for full details'''
  4. import copy, traceback
  5. from ..core.himesis import Himesis
  6. # Abstract class
  7. class Message(object): pass
  8. class TransformationException(Message, Exception):
  9. class ExceptionStatus:
  10. ACTIVE = 'active'
  11. HANDLING = 'handling'
  12. HANDLED = 'handled'
  13. '''
  14. The model of an exception occurrence.
  15. '''
  16. def __init__(self, instance=None, msg=''):
  17. super(Exception, self).__init__()
  18. self.inner_exception = instance
  19. self.msg = msg
  20. if instance and msg == '':
  21. self.msg = self.inner_exception.args[0]
  22. self.detail = ''
  23. if instance and len(self.inner_exception.args) > 1:
  24. self.detail = self.inner_exception.args[1]
  25. self.packet = None
  26. self.start_time = 0
  27. self.end_time = 0
  28. self.status = TransformationException.ExceptionStatus.ACTIVE
  29. self.transformation_unit = None
  30. self.transformation_context = None
  31. if instance:
  32. self.transformation_context = traceback.format_exc()
  33. self.debug_msg = """%s: %s
  34. Detail: %s
  35. Status: %s
  36. Start: %f - End: %f
  37. Packet: %s
  38. Unit: %s
  39. Context:%s
  40. """ % (self.inner_exception.__class__.__name__, self.msg, self.detail,
  41. self.status, self.start_time, self.end_time, self.packet,
  42. self.transformation_unit, self.transformation_context)
  43. def __str__(self):
  44. # return self.debug_msg
  45. return self.msg + '\n' + str(self.transformation_context)
  46. class Cancel(Message):
  47. '''
  48. This message is used to cancel the current activity of a primitive.
  49. '''
  50. def __init__(self):
  51. self.exclusions = [] # the primitives to not be cancelled
  52. def __str__(self):
  53. return 'Cancel - exclusion = %s' % self.exclusions
  54. class Packet(Message):
  55. '''
  56. Holds the current graph and the different matches.
  57. '''
  58. def __init__(self, graph=None):
  59. self.graph = graph # the source graph
  60. self.deltas = [] # holds the modifications produced by a rule
  61. self.match_sets = {} # holds of the matches for each pre-condition pattern already matched
  62. self.current = None # points to the guid identifying the current match set
  63. self.global_pivots = Pivots() # {pivot name: source node guid}
  64. def __str__(self):
  65. ms = ''.join(['''
  66. %s: %s''' % (k, self.match_sets[k]) for k in sorted(self.match_sets)])
  67. if ms == '':
  68. ms = str(None)
  69. s = '''Packet (%s)
  70. graph: %s
  71. deltas: %s
  72. match_sets: %s
  73. pivots: %s''' % (self.current, self.graph, self.deltas, ms, self.global_pivots)
  74. return s
  75. def clone(self):
  76. cpy = Packet()
  77. cpy.graph = self.graph.copy()
  78. cpy.deltas = self.deltas[:]
  79. cpy.global_pivots = copy.copy(self.global_pivots)
  80. cpy.current = self.current
  81. cpy.match_sets = copy.deepcopy(self.match_sets)
  82. return cpy
  83. def copy_readonly(self):
  84. cpy = Packet()
  85. cpy.graph = self.graph
  86. cpy.deltas = self.deltas
  87. cpy.global_pivots = copy.copy(self.global_pivots)
  88. cpy.current = self.current
  89. cpy.match_sets = copy.deepcopy(self.match_sets)
  90. return cpy
  91. def copy_state(self, conditionId):
  92. cpy = Packet()
  93. cpy.graph = self.graph.copy()
  94. cpy.deltas = self.deltas[:]
  95. cpy.global_pivots = copy.copy(self.global_pivots)
  96. cpy.current = self.current
  97. if conditionId in self.match_sets:
  98. cpy.match_sets = {conditionId: copy.copy(self.match_sets[conditionId])}
  99. return cpy
  100. def set_state(self, packet):
  101. self.graph = packet.graph
  102. self.deltas = packet.deltas
  103. self.global_pivots = packet.global_pivots
  104. self.current = packet.current
  105. if packet.match_sets is not None:
  106. self.match_sets.update(packet.match_sets)
  107. def clear_state(self):
  108. self.deltas = []
  109. self.match_sets = {}
  110. self.current = None
  111. self.global_pivots = Pivots()
  112. def __copy__(self):
  113. return self.copy_readonly()
  114. def __deepcopy__(self, memo):
  115. return self.__copy__()
  116. # def get_curr_matchset(self):
  117. # return self.match_sets[self.current]
  118. #
  119. # def get_match2rewrite(self, condition):
  120. # return self.match_sets[condition].matches[self.match_sets[condition].match2rewrite]
  121. #
  122. # def get_curr_match2rewrite(self):
  123. # return self.match_sets[self.current].matches[self.match_sets[self.current].match2rewrite]
  124. #
  125. # def remove_match2rewrite(self, condition):
  126. # # Remove the match to rewrite
  127. # del self.match_sets[condition].matches[self.match_sets[condition].match2rewrite]
  128. # # If the corresponding match set has become empty, remove it too
  129. # if len(self.match_sets[condition].matches) == 0:
  130. # del self.match_sets[condition]
  131. #
  132. # def get_local_pivots(self):
  133. # return self.match_sets[self.current].matches[self.match_sets[self.current].match2rewrite].local_pivots
  134. def clean(self):
  135. '''
  136. Unflags dirty matches
  137. '''
  138. for cond in self.match_sets:
  139. for match in self.match_sets[cond].matches:
  140. match.clean(self)
  141. class MatchSet:
  142. '''
  143. Holds the different matches of a pre-condition.
  144. '''
  145. def __init__(self):
  146. self.match2rewrite = None # the selected match to be transformed
  147. self.matches = [] # TODO: should it be a generator?
  148. # TODO: Should we store all the matches and let the iterator explicitly choose one randomly? Or rely on the matching algorithm and save memory space?
  149. def __str__(self):
  150. s = '''MatchSet (%s): %s''' % (self.match2rewrite, self.matches)
  151. return s
  152. def __copy__(self):
  153. cpy = MatchSet()
  154. cpy.match2rewrite = self.match2rewrite
  155. cpy.matches = [copy.copy(match) for match in self.matches]
  156. return cpy
  157. def __deepcopy__(self, memo):
  158. cpy = MatchSet()
  159. cpy.match2rewrite = self.match2rewrite
  160. cpy.matches = [copy.deepcopy(match) for match in self.matches]
  161. return cpy
  162. class Match(dict):
  163. '''
  164. Wraps the mapping from the label of a pre-condition pattern model element
  165. to the node index of the corresponding source model element.
  166. '''
  167. def __init__(self):
  168. super(Match, self).__init__() # {pattern node label : source node guid}
  169. self.local_pivots = Pivots() # {pivot name : source node guid}
  170. def __copy__(self):
  171. cpy = copy.copy(super(Match, self))
  172. cpy.local_pivots = copy.copy(self.local_pivots)
  173. return cpy
  174. def __deepcopy__(self, memo):
  175. cpy = copy.deepcopy(super(Match, self))
  176. cpy.local_pivots = copy.deepcopy(self.local_pivots)
  177. return cpy
  178. def is_dirty(self, packet):
  179. '''
  180. Determines whether a source model element is dirty.
  181. @param packet: The packet on which the mappings are bound.
  182. '''
  183. for v in self.values():
  184. node = packet.graph.get_node(v)
  185. node = packet.graph.vs[node]
  186. if node is not None:
  187. # Check dirty flag
  188. if Himesis.Constants.MT_DIRTY in node.attribute_names() and node[Himesis.Constants.MT_DIRTY]:
  189. return True
  190. else:
  191. # It was deleted
  192. return True
  193. return False
  194. def clean(self, packet):
  195. for v in self.values():
  196. node = packet.graph.get_node(v)
  197. node = packet.graph.vs[node]
  198. if node and Himesis.Constants.MT_DIRTY in node.attribute_names():
  199. node[Himesis.Constants.MT_DIRTY] = False
  200. def to_label_mapping(self, source_graph):
  201. '''
  202. Converts the match to a mapping dictionary {label: source node index}.
  203. '''
  204. mapping = {}
  205. for label in self.keys():
  206. try:
  207. sourceNode = source_graph.get_node(self[label])
  208. except KeyError:
  209. raise Exception('The matched node %s does not exist' % label)
  210. if sourceNode is not None:
  211. mapping[label] = sourceNode
  212. else:
  213. raise Exception('The matched node %s does not exist' % label)
  214. return mapping
  215. def to_mapping(self, source_graph, pattern_graph):
  216. '''
  217. Converts the match to a mapping dictionary {pattern node index: source node index}.
  218. '''
  219. mapping = {}
  220. for label in self.keys():
  221. patternNode = pattern_graph.get_node_with_label(label)
  222. if patternNode is not None:
  223. sourceNode = source_graph.get_node(self[label])
  224. mapping[patternNode] = sourceNode
  225. return mapping
  226. def from_mapping(self, mapping, source_graph, pattern_graph):
  227. '''
  228. Extracts all matches from a mapping dictionary {pattern node index: source node index}
  229. and adds them to this object in the form {pattern label: source node guid}.
  230. Relevant pivots are also extracted.
  231. '''
  232. for pattern_node in mapping:
  233. #print "Pattern Graph: ", pattern_graph
  234. #print "len(Pattern Graph.vs): ", pattern_graph.vcount()
  235. #print "Pattern Node: ", pattern_node
  236. if pattern_node < pattern_graph.vcount():
  237. #print "Pattern Graph.vs[pattern_node]: ", pattern_graph.vs[pattern_node]
  238. label = pattern_graph.vs[pattern_node][Himesis.Constants.MT_LABEL]
  239. guid = source_graph.vs[mapping[pattern_node]][Himesis.Constants.GUID]
  240. self[label] = guid
  241. self.local_pivots.from_mapping(mapping, source_graph, pattern_graph)
  242. class Pivots(dict):
  243. '''
  244. Wraps the binding from a pivot name to a source model element.
  245. '''
  246. def __init__(self):
  247. super(Pivots, self).__init__() # {pivot name : source node guid}
  248. self.has_source_node_indices = False
  249. def __copy__(self):
  250. cpy = copy.copy(super(Pivots, self))
  251. cpy.has_source_node_indices = self.has_source_node_indices
  252. return cpy
  253. def __deepcopy__(self, memo):
  254. cpy = copy.deepcopy(super(Pivots, self))
  255. cpy.has_source_node_indices = self.has_source_node_indices
  256. return cpy
  257. def to_source_node_indices(self, source_graph):
  258. for p in self.keys():
  259. sourceNode = source_graph.get_node(self[p])
  260. self[p] = sourceNode
  261. self.has_source_node_indices = True
  262. def to_mapping(self, source_graph, pattern_graph):
  263. '''
  264. Converts the pivots to a mapping dictionary {pattern node index: source node index}.
  265. '''
  266. mapping = {}
  267. if not self.has_source_node_indices:
  268. for p in self.keys():
  269. patternNode = pattern_graph.get_pivot_in(p)
  270. if patternNode is not None:
  271. sourceNode = source_graph.get_node(self[p])
  272. mapping[patternNode] = sourceNode
  273. else:
  274. for p in self.keys():
  275. patternNode = pattern_graph.get_pivot_in(p)
  276. if patternNode is not None:
  277. mapping[patternNode] = self[p]
  278. return mapping
  279. def from_mapping(self, mapping, source_graph, pattern_graph):
  280. '''
  281. Extracts all pivots from a mapping dictionary {pattern node index: source node index}
  282. and adds them to this object in the form {pivot name: source node guid}.
  283. '''
  284. for p in mapping:
  285. pivot = pattern_graph.get_pivot_out(p)
  286. if pivot is not None:
  287. guid = source_graph.vs[mapping[p]][Himesis.Constants.GUID]
  288. if guid is not None:
  289. self[pivot] = guid
  290. else:
  291. #TODO: This should be a TransformationLanguageSpecificException
  292. raise Exception('The bound node has no Guid')
  293. # Define the nil packet
  294. NIL_PACKET = Packet()