basicmsc.py 13.8 KB
Newer Older
Maxime Perrotin's avatar
Maxime Perrotin committed
1 2 3 4 5 6 7 8
#******************************************************************************
#
# 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
9
#  modify it under the terms of the GNU Lesser General Public License as published
Maxime Perrotin's avatar
Maxime Perrotin committed
10 11 12 13 14 15
#  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
16
#  GNU Lesser General Public License for more details.
Maxime Perrotin's avatar
Maxime Perrotin committed
17
#
18
#  You should have received a copy of the GNU Lesser General Public License
Maxime Perrotin's avatar
Maxime Perrotin committed
19 20 21 22 23
#  along with TASTE Msc Diagram Editor.  If not, see
#  <http://www.gnu.org/licenses/>.
#
#  Author: Angel Esquinas <aesquina@datsi.fi.upm.es>
#
24
#  Copyright (c) 2012 UPM and European Space Agency
Maxime Perrotin's avatar
Maxime Perrotin committed
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
#
#******************************************************************************
u"""
This class represent a **Basic Message Sequence Chart** *(BMSC)* element. It
have all the instances contains in the BMSC.

A Basic MSC must have all the elements used by itself as child.

The Basic MSC is a type of MSC that can be part of
:class:`~msccore.MscDocument`.

The Basic MSC can have events, but unlike :class:`~msccore.MscInstances` only
can have messages events. This message events are associated with *gates* in
the ITU-T Z.120 recommendation.

The messages (:class:`msccore.MscMessage`) added with
:meth:`BasicMsc.addMessage` only are setup as children or this BMSC. This is
neccesary because the messages can not be children of instances because one
message can be associated with the **sender** and ther **receiver**. The
message can be part of the BMSC as event, but in this case the message event
is added with :meth:`BasicMsc.addEvent`.
"""
import logging

from msc import Msc
from mscevent import MscEvent

52
#logging.basicConfig()
Maxime Perrotin's avatar
Maxime Perrotin committed
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
logger = logging.getLogger(__name__)

class BasicMsc(Msc):

    u"""
    Constructs a new *Basic Message Sequence Chart* object.

    :param unicode name: Name of BMSC
    :param parent:
    :type parent: PySide.QtCore.QObject

    The `name` and `parent` parameters given as arguments are passed to the
    parent constructor.
    """

    TYPE = 'BMsc'

    def __init__(self, name, parent=None):
        u"""
        Construct
        """
        self.TYPE = self.TYPE
        super(BasicMsc, self).__init__(name, parent)

        self._instances = []
        self._events = []
        # Auxiliar Variable to save numbers of events  when load
        self.eventsOnLoad = None

    def addInstance(self, inst):
        u"""
        Add instance `inst` as instance of this BMSC.

        :param inst: Instance to add
        :type inst: msccore.MscInstance
        :rtype: Boolean

        The :meth:mscelement._contentsChanged function is called to indicate
        that the content of this basic MSC are changed.
        """
        # Check if the instance is child of parent
        if self._instances.count(inst) > 0:
            logger.warning("Trying to add existing instance to basic msc")
            return True

        inst.setParent(self)
        self._instances.append(inst)

        # When instance will be deleted then removed it
        inst.deleted.connect(self.removeInstance)
        inst.contentsChanged.connect(self._contentsChanged)

        logger.debug("Added instance '%(iname)s' to msc '%(mscname)s",
                     {'iname': inst.name(), 'mscname': self.name()})

        self._contentsChanged()
        return True

    def removeInstance(self, inst):
        u"""
        Remove instance `inst` from this basic MSC.

        :param inst: Instance to remove
        :type inst: msccore.MscInstance
        :rtype: Boolean

        If `inst`, given as argument, is not include within this basic MSC
        then `False` is returned.

        The :meth:mscelement._contentsChanged function is called to indicate
        that the content of this basic MSC are changed.
        """
        if self._instances.count(inst) == 0:
126
            logger.warning("Trying to remove not associated instance to this "
Maxime Perrotin's avatar
Maxime Perrotin committed
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413
                           "basic msc")
            return False
        # Check if instance is child of me
        inst.deleted.disconnect(self.removeInstance)
        inst.contentsChanged.disconnect(self._contentsChanged)

        self._instances.remove(inst)
        inst.setParent(None)

        logger.debug("Removed instance '%(iname)s' to msc '%(mscname)s",
                     {'iname': inst.name(), 'mscname': self.name()})
        # Content Change
        self._contentsChanged()
        return True

    def instances(self):
        u"""
        Return a list with all the instances within objects contain in
        this basic MSC.

        :rtype: list of msccore.MscInstance
        """
        return self._instances[:]

    def deleteInstances(self):
        u"""
        Remove all instances included within this :class:`~msccore.BasicMsc`.
        """
        logger.debug("Deleting all instances of msc '%s'", self.name())
        for i in self.instances():
            self.removeInstance(i)
            i.delete()

    def addMessage(self, msg):
        u"""
        Add a `msg` :class:`~msccore.MscMessage` to this basic MSC.

        :param msg: Message to add to this basic MSC.
        :type msg: msccore.MscMessage
        :rtype: Boolean

        The message is added as child of this Basic Msc. Because of this it
        can be used as event within this MSC.

        The :meth:mscelement._contentsChanged function is called to indicate
        that the content of this basic MSC are changed.
        """
        if msg.parent() == self:
            return True

        msg.setParent(self)
        # self.elementAdded(msg)
        msg.deleted.connect(self.removeMessage)
        msg.contentsChanged.connect(self._contentsChanged)

        logger.debug("Added message '%(mname)s' to msc '%(mscname)s",
                     {'mname': msg.name(), 'mscname': self.name()})
        # Change Content
        self._contentsChanged()
        return True

    def removeMessage(self, msg):
        u"""
        Remove message `msg` from this basic MSC.

        :param msg: Message to remove
        :type msg: msccore.MscMessage
        :rtype: Boolean

        If `msg`, given as argument, is not include within this basic MSC
        then `False` is returned.

        The :meth:mscelement._contentsChanged function is called to indicate
        that the content of this basic MSC are changed.
        """
        if msg.parent() != self:
            logger.warning("Trying to remove message '%(mname)s' from basic "
                           "msc '%(mscname)s' that is not associated",
                         {'mname': msg.name(), 'mscname': self.name()})
            return False
        msg.setParent(None)
        # Disconnect deleted message
        msg.deleted.disconnect(self.removeMessage)
        msg.contentsChanged.disconnect(self._contentsChanged)

        logger.debug("Removed message '%(mname)s' from msc '%(mscname)s",
                     {'mname': msg.name(), 'mscname': self.name()})

        # Change content
        self._contentsChanged()
        return True

    def instanceName(self):
        u"""
        Return the name of this Basic MSC.

        :rtype: unicode

        This functions is only a wrapper of :meth:`name` to be compatible
        with the way to control **events** within instances. It is necessary
        because basics MSCs can send/receive messages.
        """
        return self.name()

    #*************************************************************************
    # Messages or Events
    #*************************************************************************
    def addEvent(self, elem, pos=None):
        u"""
        Add new :class:`~msccore.MscEvent` to this MSC. New event is created
        with `elem` as content.

        :param elem: Element to create the new event.
        :type elem: msccore.MscElement
        :rtype: msccore.MscEvent

        Although `elem` can be any :class:`~msccore.MscElement` object only
        "event" elements must be used as argument. Until now, within MSC,
        this elements are:

        * :class:`~msccore.MscMessage`

        The created event is returned.

        The :meth:`MscElement._contentsChanged` function is called to indicate
        that the content of this basic MSC are changed.

        .. warning::
           Event only must be in one basic MSC or instance at the same time.
        """
        # TODO: Allow positional creation
        event = MscEvent(elem, self)
        # Connect 'deleted' signal
        event.deleted.connect(self._eventDeleted)
        event.contentsChanged.connect(self._contentsChanged)

        self._events.append(event)  # Put event in the last position
        logger.debug("Added event to basic Msc with element '%s'",
                     elem.name())

        self._contentsChanged()
        return event

    def removeEvent(self, event):
        u"""
        Remove event `event` from this basic MSC.

        :param event: Event to remove
        :type event: msccore.MscEvent
        :rtype: Boolean

        The event is only removed from this MSC, but it is not destroyed. The
        `parent` of the event is set to `None`.

        If `event`, given as argument, is not include within this basic MSC
        then `False` is returned.

        The :meth:`mscelement._contentsChanged` function is called to indicate
        that the content of this basic MSC are changed.

        .. warning::
           Event only must be in one basic MSC or instance at the same time.
        """
        if self._events.count(event) == 0:
            logger.error("Trying to remove event '%(ename)s' from basic msc "
                         "'%(mname)' which are not associated",
                         {'ename': event.name(), 'mname': self.name()})
            return False
        self._events.remove(event)
        # Disconnect 'deleted' signal and event haven't parent yet
        event.deleted.disconnect(self._eventDeleted)
        event.contentsChanged.disconnect(self._contentsChanged())
        event.setParent(None)
        logger.debug("Removed event '%(ename)s' from msc '%(mscname)s",
                     {'ename': event.name(), 'mscname': self.name()})
        self._contentsChanged()
        return True

    def delEvent(self, elem):
        u"""
        Remove and destroy the `event` associated with the element `elem`.

        :param elem: Msc element that are associated with the event.
        :type elem: msccore.MscElement
        :rtype: Boolean

        Return `True` if exist event associated with the `elem` given as
        argument, and it has been destroyed. In other case return `False`.

        .. note::
           The element associated with the event, if any, is destroyed.

        Although `elem` can be any :class:`~msccore.MscElement` object only
        "event" elements must be used as argument. Until now,
        this elements are:

        * :class:`~msccore.MscMessage`

        The :meth:`MscElement._contentsChanged` function is called to indicate
        that the content of this basic MSC are changed.
        """
        for event in self._events:
            if event.isElement(elem):
                self.removeEvent(event)
                # Delete the Event
                event.delete()
                return True
        return False

    def events(self):
        u"""
        Return a list with all events included in this basic MSC.

        :rtype: list of msccore.MscEvents

        The list returned are ordered. The :meth:`BasicMsc.reorderEvents`
        method is used to order the events.
        """
        self.reorderEvents()
        return self._events

    def reorderEvents(self):
        u"""
        Reorder the list of events, depending of the absolute
        position of the element associated with the event.
        """
        self._events.sort(key=lambda event: event.pos())

    def _eventDeleted(self, event):
        """
        Remove the event 'event'. It will be deleted. This function is the
        handler for 'deleted' signal of 'event'
        """
        self.removeEvent(event)

    ###########################################################################
    # Delete
    ###########################################################################
    def delete(self):
        u"""
        Delete this basic MSC.

        The events, instances and messages included within this basic MSC are
        deleted before it will be destroy.

        The :meth:`MscElement.delete` method is called at begining of this
        method.

        .. note::
           After this basic MSC is delete, its references is not valid. It can
           not be used.
        """
        # Tell that I will be deleted
        self.deleted.emit(self)
        self.deleteInstances()
        for event in self._events:
            self.removeEvent(event)
            event.delete()
        # When remove instances, messages are deleted
        self.deleteLater()

    #**************************************************************************
    # Visitor Pattern
    #**************************************************************************
    def accept(self, visitor):
        u"""
        Implementation of visitor pattern for :class:`msccore.BasicMsc`.

        :param visitor:
        :type visitor: :class:`msccore.MscVisitor`

        This function call :meth:`msccore.MscVisitor.visitorBMSC`.
        """
        visitor.visitorBMSC(self)

    def show(self, n=1):
        tab = ''
        i = 1
        while (i < n):
            tab = tab + '  '
            i = i + 1
        print tab + '{0}'.format(self)
        self.reorderEvents()
        for i in self._events:
            i.show(n + 1)
        for i in self._instances:
            i.show(n + 1)