123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358 |
- '''*****************************************************************************
- 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 <http://www.gnu.org/licenses/>.
- *****************************************************************************'''
- 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()
|