'''***************************************************************************** AToMPM - A Tool for Multi-Paradigm Modelling Copyright (c) 2011 Eugene Syriani This file is part of AToMPM. AToMPM is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. AToMPM is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with AToMPM. If not, see . *****************************************************************************''' import copy, traceback from ..core.himesis import Himesis # Abstract class class Message(object): pass class TransformationException(Message, Exception): class ExceptionStatus: ACTIVE = 'active' HANDLING = 'handling' HANDLED = 'handled' ''' The model of an exception occurrence. ''' def __init__(self, instance=None, msg=''): super(Exception, self).__init__() self.inner_exception = instance self.msg = msg if instance and msg == '': self.msg = self.inner_exception.args[0] self.detail = '' if instance and len(self.inner_exception.args) > 1: self.detail = self.inner_exception.args[1] self.packet = None self.start_time = 0 self.end_time = 0 self.status = TransformationException.ExceptionStatus.ACTIVE self.transformation_unit = None self.transformation_context = None if instance: self.transformation_context = traceback.format_exc() self.debug_msg = """%s: %s Detail: %s Status: %s Start: %f - End: %f Packet: %s Unit: %s Context:%s """ % (self.inner_exception.__class__.__name__, self.msg, self.detail, self.status, self.start_time, self.end_time, self.packet, self.transformation_unit, self.transformation_context) def __str__(self): # return self.debug_msg return self.msg + '\n' + str(self.transformation_context) class Cancel(Message): ''' This message is used to cancel the current activity of a primitive. ''' def __init__(self): self.exclusions = [] # the primitives to not be cancelled def __str__(self): return 'Cancel - exclusion = %s' % self.exclusions class Packet(Message): ''' Holds the current graph and the different matches. ''' def __init__(self, graph=None): self.graph = graph # the source graph self.deltas = [] # holds the modifications produced by a rule self.match_sets = {} # holds of the matches for each pre-condition pattern already matched self.current = None # points to the guid identifying the current match set self.global_pivots = Pivots() # {pivot name: source node guid} def __str__(self): ms = ''.join([''' %s: %s''' % (k, self.match_sets[k]) for k in sorted(self.match_sets)]) if ms == '': ms = str(None) s = '''Packet (%s) graph: %s deltas: %s match_sets: %s pivots: %s''' % (self.current, self.graph, self.deltas, ms, self.global_pivots) return s def clone(self): cpy = Packet() cpy.graph = self.graph.copy() cpy.deltas = self.deltas[:] cpy.global_pivots = copy.copy(self.global_pivots) cpy.current = self.current cpy.match_sets = copy.deepcopy(self.match_sets) return cpy def copy_readonly(self): cpy = Packet() cpy.graph = self.graph cpy.deltas = self.deltas cpy.global_pivots = copy.copy(self.global_pivots) cpy.current = self.current cpy.match_sets = copy.deepcopy(self.match_sets) return cpy def copy_state(self, conditionId): cpy = Packet() cpy.graph = self.graph.copy() cpy.deltas = self.deltas[:] cpy.global_pivots = copy.copy(self.global_pivots) cpy.current = self.current if conditionId in self.match_sets: cpy.match_sets = {conditionId: copy.copy(self.match_sets[conditionId])} return cpy def set_state(self, packet): self.graph = packet.graph self.deltas = packet.deltas self.global_pivots = packet.global_pivots self.current = packet.current if packet.match_sets is not None: self.match_sets.update(packet.match_sets) def clear_state(self): self.deltas = [] self.match_sets = {} self.current = None self.global_pivots = Pivots() def __copy__(self): return self.copy_readonly() def __deepcopy__(self, memo): return self.__copy__() # def get_curr_matchset(self): # return self.match_sets[self.current] # # def get_match2rewrite(self, condition): # return self.match_sets[condition].matches[self.match_sets[condition].match2rewrite] # # def get_curr_match2rewrite(self): # return self.match_sets[self.current].matches[self.match_sets[self.current].match2rewrite] # # def remove_match2rewrite(self, condition): # # Remove the match to rewrite # del self.match_sets[condition].matches[self.match_sets[condition].match2rewrite] # # If the corresponding match set has become empty, remove it too # if len(self.match_sets[condition].matches) == 0: # del self.match_sets[condition] # # def get_local_pivots(self): # return self.match_sets[self.current].matches[self.match_sets[self.current].match2rewrite].local_pivots def clean(self): ''' Unflags dirty matches ''' for cond in self.match_sets: for match in self.match_sets[cond].matches: match.clean(self) class MatchSet: ''' Holds the different matches of a pre-condition. ''' def __init__(self): self.match2rewrite = None # the selected match to be transformed self.matches = [] # TODO: should it be a generator? # 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? def __str__(self): s = '''MatchSet (%s): %s''' % (self.match2rewrite, self.matches) return s def __copy__(self): cpy = MatchSet() cpy.match2rewrite = self.match2rewrite cpy.matches = [copy.copy(match) for match in self.matches] return cpy def __deepcopy__(self, memo): cpy = MatchSet() cpy.match2rewrite = self.match2rewrite cpy.matches = [copy.deepcopy(match) for match in self.matches] return cpy class Match(dict): ''' Wraps the mapping from the label of a pre-condition pattern model element to the node index of the corresponding source model element. ''' def __init__(self): super(Match, self).__init__() # {pattern node label : source node guid} self.local_pivots = Pivots() # {pivot name : source node guid} def __copy__(self): cpy = copy.copy(super(Match, self)) cpy.local_pivots = copy.copy(self.local_pivots) return cpy def __deepcopy__(self, memo): cpy = copy.deepcopy(super(Match, self)) cpy.local_pivots = copy.deepcopy(self.local_pivots) return cpy def is_dirty(self, packet): ''' Determines whether a source model element is dirty. @param packet: The packet on which the mappings are bound. ''' for v in self.itervalues(): node = packet.graph.get_node(v) node = packet.graph.vs[node] if node is not None: # Check dirty flag if Himesis.Constants.MT_DIRTY in node.attribute_names() and node[Himesis.Constants.MT_DIRTY]: return True else: # It was deleted return True return False def clean(self, packet): for v in self.itervalues(): node = packet.graph.get_node(v) node = packet.graph.vs[node] if node and Himesis.Constants.MT_DIRTY in node.attribute_names(): node[Himesis.Constants.MT_DIRTY] = False def to_label_mapping(self, source_graph): ''' Converts the match to a mapping dictionary {label: source node index}. ''' mapping = {} for label in self.iterkeys(): try: sourceNode = source_graph.get_node(self[label]) except KeyError: raise Exception('The matched node %s does not exist' % label) if sourceNode is not None: mapping[label] = sourceNode else: raise Exception('The matched node %s does not exist' % label) return mapping def to_mapping(self, source_graph, pattern_graph): ''' Converts the match to a mapping dictionary {pattern node index: source node index}. ''' mapping = {} for label in self.iterkeys(): patternNode = pattern_graph.get_node_with_label(label) if patternNode is not None: sourceNode = source_graph.get_node(self[label]) mapping[patternNode] = sourceNode return mapping def from_mapping(self, mapping, source_graph, pattern_graph): ''' Extracts all matches from a mapping dictionary {pattern node index: source node index} and adds them to this object in the form {pattern label: source node guid}. Relevant pivots are also extracted. ''' for pattern_node in mapping: #print "Pattern Graph: ", pattern_graph #print "Pattern Graph.vs: ", pattern_graph.vs #print "Pattern Node: ", pattern_node if pattern_node < len(pattern_graph.vs): #print "Pattern Graph.vs[pattern_node]: ", pattern_graph.vs[pattern_node] label = pattern_graph.vs[pattern_node][Himesis.Constants.MT_LABEL] guid = source_graph.vs[mapping[pattern_node]][Himesis.Constants.GUID] self[label] = guid self.local_pivots.from_mapping(mapping, source_graph, pattern_graph) class Pivots(dict): ''' Wraps the binding from a pivot name to a source model element. ''' def __init__(self): super(Pivots, self).__init__() # {pivot name : source node guid} self.has_source_node_indices = False def __copy__(self): cpy = copy.copy(super(Pivots, self)) cpy.has_source_node_indices = self.has_source_node_indices return cpy def __deepcopy__(self, memo): cpy = copy.deepcopy(super(Pivots, self)) cpy.has_source_node_indices = self.has_source_node_indices return cpy def to_source_node_indices(self, source_graph): for p in self.iterkeys(): sourceNode = source_graph.get_node(self[p]) self[p] = sourceNode self.has_source_node_indices = True def to_mapping(self, source_graph, pattern_graph): ''' Converts the pivots to a mapping dictionary {pattern node index: source node index}. ''' mapping = {} if not self.has_source_node_indices: for p in self.iterkeys(): patternNode = pattern_graph.get_pivot_in(p) if patternNode is not None: sourceNode = source_graph.get_node(self[p]) mapping[patternNode] = sourceNode else: for p in self.iterkeys(): patternNode = pattern_graph.get_pivot_in(p) if patternNode is not None: mapping[patternNode] = self[p] return mapping def from_mapping(self, mapping, source_graph, pattern_graph): ''' Extracts all pivots from a mapping dictionary {pattern node index: source node index} and adds them to this object in the form {pivot name: source node guid}. ''' for p in mapping: pivot = pattern_graph.get_pivot_out(p) if pivot is not None: guid = source_graph.vs[mapping[p]][Himesis.Constants.GUID] if guid is not None: self[pivot] = guid else: #TODO: This should be a TransformationLanguageSpecificException raise Exception('The bound node has no Guid') # Define the nil packet NIL_PACKET = Packet()