#******************************************************************************
#
# TASTE Msc Diagram Editor
# http://taste.tuxfamily.org/
#
# This file is part of TASTE Msc Editor.
#
# TASTE Msc Diagram Editor 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.
#
# TASTE Msc Diagram Editor 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 TASTE Msc Diagram Editor. If not, see
# .
#
# Author: Angel Esquinas
#
# Copyright (c) 2012 UPM and European Space Agency
#
#******************************************************************************
u"""
The :class:`msccore.MscMessage` class represents a message within an MSC. It
is a relation between an output and an input. The output may come from either
the environment (through a gate) or an instance; and the input comes either
from the environment(through a gate) or an instance.
A gate is defined as the message event from/to a basic MSC
(:class:`msccore.BasicMsc`).
Messages are events. Messages can be `elem` within
:class:`msccore.MscEvent`.
Messages have an output (**sender**) and an input (**receiver**), these
can be set as `None`. These message properties can be accessed with
:meth:`MscMessage.setSender`, :meth:`MscMessage.setReceiver`,
:meth:`MscMessage.messageSender` and :meth:`MscMessage.messageReceiver`. The
`sender` and `receiver` objects must allow `events` and be a subclass of
:class:`msccore.MscElement`. These classes are:
* :class:`msccore.BasicMsc`
* :class:`msccore.MscInstance`
Messages can be an instance of message kind
(:class:`msccore.MscMessageDecl`). Message kind declares the
number of parameters, its types and the name of the message.
When a message is associated with message kind the name of the message
is the name of the kind.
In case a message is an instance of message kind, the `values` property,
, that it a list of values, must have the same number of elements than the
number of parameters declared within the kind. Each `value` can be empty but
it must have space allocated. The position of a given value is associated with
the type declared in the same position within the types list::
types = ["str", "int", "str"] # Declared in MscMessageDecl (type of message)
values = ["hello", "2", "taste"] # Declared in MscMessage (message)
"""
import logging
from PySide.QtCore import Signal
from mscelement import MscElement
logger = logging.getLogger(__name__)
class MscMessage(MscElement):
u"""
Constructs a new :class:`msccore.MscMessage` object.
:param start: output of message.
:param end: input of message.
:param unicode name: name of message
:param int pos: position of message
:param parent: parent of message
:type start: msccore.BasicMsc or msccore.MscInstance
:type end: msccore.BasicMsc or msccore.MscInstance
The `name` and `parent` parameters given as arguments are passed to the
parent constructor.
"""
TYPE = 'MscMessage'
senderChanged = Signal()
"""
:attr:`senderChanged` is emitted when the sender of this message
change.
"""
receiverChanged = Signal()
"""
:attr:`receiverChanged` is emitted when the receiver of this message
change
"""
valuesChanged = Signal()
u"""
:attr:`valueChanged` is emitted when the values are changed.
.. note::
Change message type reset the values. It emit :attr:`valueChanged`.
.. seealso::
:meth:`MscMessage.resetValues`, :meth:`MscMessage.setValues`
"""
def __init__(self, start=None, end=None, name='', pos=0, parent=None):
'''
Constructor
'''
# This parameters are for all the ways as message can be showed
self._typeMessage = None
self._values = [] # List of values of parameters associated
super(MscMessage, self).__init__(name, parent, self.TYPE)
self.setSendInstance(start)
self.setRecInstance(end)
self.setAbsPos(pos)
def name(self):
u"""
Return the name of message.
:rtype: unicode
If message has message type associated, then the name returned is the
name of type associated.
"""
if self.typeMessage() == None:
return super(MscMessage, self).name()
else:
return self.typeMessage().name()
def setSendInstance(self, inst):
#TODO: Move source to `setSender`.
#TODO: Check if instance is set, and delete message for it
self.msgSender = inst
if inst != None:
inst.addEvent(self)
self.senderChanged.emit() # Sender is modified
self.dataChanged() # Property is modified
def setSender(self, send=None):
u"""
Set the sender.
:param send: Sender of the message.
This property holds the sender of message.
:attr:`MscMessage.senderChanged` signal is emitted when call to this
function.
.. seealso::
:meth:`MscMessage.messageSender`
"""
self.setSendInstance(send)
def setRecInstance(self, inst):
#TODO: Check if instance is set, and delete message for it
self.msgRecv = inst
if inst != None:
inst.addEvent(self)
self.receiverChanged.emit()
self.dataChanged()
def setReceiver(self, recv):
u"""
Set the receiver of message.
:param recv: Receiver of message.
This property holds the receiver of message.
:attr:`MscMessage.receiverChanged` signal is emitted when call to this
function.
.. seealso::
:meth:`MscMessage.messageReceiver`
"""
self.setRecInstance(recv)
def instanceSender(self):
return self.msgSender
def messageSender(self):
u"""
Return the *sender* of message.
:rtype: msccore.MscElement (that allow events)
If not sender is set then `None` is returned.
"""
return self.instanceSender()
def instanceReceiver(self):
return self.msgRecv
def messageReceiver(self):
u"""
Return the *receiver* of message.
:rtype: msccore.MscElement (that allow events)
If not receiver is set then `None` is returned.
"""
return self.instanceReceiver()
def setMessageType(self, msgType=None):
u"""
Associate message with `msgType` message type.
:param msgType: New message type.
:type msgType: msccore.MscMessageDecl
.. note::
As result of call this function the `values` property of message
is initialize as new list of empty string.
:meth:`MscMessage.resetValues` is called.
"""
if self._typeMessage != None:
# Disconnect signals
self._typeMessage.dataHasChanged.disconnect(
self._typeMessageChanged)
self._typeMessage.deleted.disconnect(self._msgTypeDeleted)
self._typeMessage = msgType
if self._typeMessage != None:
# Connect data changed signal and deleted signal
self._typeMessage.dataHasChanged.connect(
self._typeMessageChanged)
self._typeMessage.deleted.connect(self._msgTypeDeleted)
self.resetValues()
self.dataChanged()
def setTypeMessage(self, msgType=None):
self.setMessageType(msgType)
def messageType(self):
u"""
Return the message type associated with the message.
:rtype: msccore.MscMessageDecl
If no message type is set, then `None` is returned.
"""
return self._typeMessage
def typeMessage(self):
return self.messageType()
def _typeMessageChanged(self, ):
""" Handler when type of message data has changed """
self.resetValues()
def _msgTypeDeleted(self, msgType):
u""" Handler when message type of this message is deleted """
self.setMessageType(None)
def resetValues(self):
u"""
Reset value of `values` property.
If :class:`~msccore.MscMessage` is associated with a message type then
`values` property is initialize to list of empty string. List length is
equal to numbers of parameters declared in message type associated.
In other case the `values` property is set as empty list.
"""
self._values = []
if self._typeMessage != None:
for i in range(self._typeMessage.numberOfParameters()):
# print "Value {0}".format(i)
self._values.append("")
self.dataChanged()
def values(self):
u"""
Return `values` property.
`values` holds the values of the parameters associated with message.
This property only have sense if message has a message type associated.
"""
return self._values
def setValues(self, list):
u"""
Set the values with the content in list.
:param list: List with values
:type list: list of unicodes
.. note::
To change only one value, first you must call values() to obtain the
values, second change the value and set the values calling this
function
.. warning::
`list` argument must have the same lenght as parameters are declared
in the message type.
"""
self._values = list[:]
self.dataChanged()
self.valuesChanged.emit()
#Order/Graphical Pos
def setAbsPos(self, pos):
u"""
Set order position of message.
:param int pos: Position of message within a event area.
`absolutePos` property holds the absolute position of message in a
list of events. `absolutePos` property only have sense if message
are used within a :class:`msccore.MscEvent` as `elem` property of
the event. The position is given in absolute order, the position is not
only relative as one unique list of events, the position is relative
to all events in one :class:`msccore.BasicMsc`.
"""
self._absPos = pos
def absPos(self):
u"""
Return the absolute order position.
:rtype: int
`absolutePos` property holds the absolute position of message in a
list of events. `absolutePos` property only have sense if message
are used within a :class:`msccore.MscEvent` as `elem` property of
the event. The position is given in absolute order, the position is not
only relative as one unique list of events, the position is relative
to all events in one :class:`msccore.BasicMsc`.
"""
return self._absPos
def delete(self):
u"""
Delete message.
Method :meth:`MscElement.delete` is called at the begining of this
method.
.. note::
After this basic MSC is delete, its references is not valid. It can
not be used.
"""
logger.debug("Deleting message: %(mname)s", {'mname': self.name()})
self.deleted.emit(self)
logger.debug("Deleted message")
self.deleteLater()
def accept(self, visitor):
u"""
Implementation of visitor pattern for :class:`msccore.MscMessage`.
:param visitor:
:type visitor: :class:`msccore.MscVisitor`
This function call :meth:`msccore.MscVisitor.visitorMscMessage`.
"""
visitor.visitorMscMessage(self)
def textual(self, msgInstanceName=None):
u"""
Return the textual format of message with parameters value.
:param msgInstanceName: Name to uniquely identifies this message.
:rtype: unicode
Example:
* If message has values associated with parameters:
msg_1(8, "par_text_1")
* If message has not values associated with parameters but have \
parameters:
msg_1()
* If message has not parameters associated:
msg_1
If `msgInstanceName` is other than 'None', then message name is
followed for ',msgInstanceName'.
Example:
* If message with the same name is used more than one time within
the same :class:`~msccore.MscMessage`. Then the function must be
called with different `msgInstanceName` each time.
With `msgInstanceName` set to `a` then:
msg_1,a(8, "par_text_1")
.. note::
The textual form return is conform to ITU-T Z.120 MSC
recommendation.
"""
if self.typeMessage() != None:
if self.typeMessage().numberOfParameters() > 0:
parValues = '(' + u', '.join(self.values()) + ')'
else:
parValues = ''
if msgInstanceName == None:
text = u"{name}{values}".format(name=self.getName(),
values=parValues)
else:
text = u"{name},{msgIn}{values}".format(name=self.getName(),
values=parValues,
msgIn=msgInstanceName)
else:
text = self.getName()
return text
def hash(self):
return "<>".format(self.getName(),
self.msgSender,
self.msgRecv)
def __str__(self):
return ("<