messages.py 13 KB

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