Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/modules/notifications/NotificationQueue.cc @ 9257

Last change on this file since 9257 was 9253, checked in by landauf, 13 years ago

fixed crash in NotificationQueue - iterators are not guaranteed to be accessible after erasing them from the set

  • Property svn:eol-style set to native
File size: 16.8 KB
RevLine 
[2280]1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
3 *                    > www.orxonox.net <
4 *
5 *
6 *   License notice:
7 *
8 *   This program is free software; you can redistribute it and/or
9 *   modify it under the terms of the GNU General Public License
10 *   as published by the Free Software Foundation; either version 2
11 *   of the License, or (at your option) any later version.
12 *
13 *   This program is distributed in the hope that it will be useful,
14 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *   GNU General Public License for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program; if not, write to the Free Software
20 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 *
22 *   Author:
23 *      Damian 'Mozork' Frick
24 *   Co-authors:
25 *      ...
26 *
27 */
28
[2911]29/**
[7403]30    @file NotificationQueue.cc
[2911]31    @brief Implementation of the NotificationQueue class.
32*/
33
[2280]34#include "NotificationQueue.h"
35
[7403]36#include <map>
37#include <sstream>
38
39#include "core/CoreIncludes.h"
[8706]40#include "core/XMLPort.h"
[7403]41#include "util/SubString.h"
[2280]42
[2435]43namespace orxonox
44{
[2910]45
[8706]46    CreateFactory(NotificationQueue);
[9253]47
[2911]48    /**
49    @brief
[8706]50        Default constructor. Registers and initializes the object.
51    @param creator
52        The creator of the NotificationQueue.
53    */
54    NotificationQueue::NotificationQueue(BaseObject* creator) : BaseObject(creator), Synchronisable(creator), registered_(false)
55    {
56        RegisterObject(NotificationQueue);
57
58        this->size_ = 0;
59        this->tickTime_ = 0.0f;
60        this->maxSize_ = NotificationQueue::DEFAULT_SIZE;
61        this->displayTime_ = NotificationQueue::DEFAULT_DISPLAY_TIME;
62
63        this->creationTime_ = std::time(0);
[9253]64
[8706]65        this->registerVariables();
66    }
67
68    // TODO move to docu.
69    /**
70    @brief
71        Constructor. Registers and initializes the object.
72    @param creator
73        The creator of the NotificationQueue
[7403]74    @param name
[7484]75        The name of the new NotificationQueue. It needs to be unique
[7403]76    @param senders
77        The senders that are targets of this NotificationQueue, i.e. the names of senders whose Notifications this NotificationQueue displays.
78        The senders need to be seperated by commas.
79    @param size
80        The size (the maximum number of displayed Notifications) of this NotificationQueue.
81    @param displayTime
82        The time during which a Notification is (at most) displayed.
[2911]83    */
[7163]84
[2911]85    /**
86    @brief
[7403]87        Destructor.
[2911]88    */
[7403]89    NotificationQueue::~NotificationQueue()
[2911]90    {
[7403]91        this->targets_.clear();
[2911]92
[8706]93        if(this->isRegistered()) // If the NotificationQueue is registered.
[7403]94        {
[7488]95            this->clear(true);
[2911]96
[7403]97            // Unregister with the NotificationManager.
98            NotificationManager::getInstance().unregisterQueue(this);
[7488]99        }
100    }
[7403]101
[7488]102    /**
103    @brief
[8706]104        Is called when the name of the NotificationQueue has changed.
105        Clears and re-creates the NotificationQueue.
[7488]106    */
[8706]107    void NotificationQueue::changedName(void)
[7488]108    {
[8706]109        SUPER(NotificationQueue, changedName);
[7488]110
[8706]111        if(this->isRegistered())
112            this->clear();
[9253]113
[8706]114        this->create();
[8079]115
[8706]116        this->targetsChanged();
117        this->maxSizeChanged();
118        this->displayTimeChanged();
[2911]119    }
120
121    /**
122    @brief
[8706]123        Creates the NotificationQueue.
[2911]124    */
[7403]125    void NotificationQueue::create(void)
[2911]126    {
[8706]127        // Register the NotificationQueue with the NotificationManager.
128        bool queueRegistered = NotificationManager::getInstance().registerQueue(this);
129        this->registered_ = true;
130        if(!queueRegistered) // If the registration has failed.
131        {
132            this->registered_ = false;
[8858]133            orxout(internal_error, context::notifications) << "NotificationQueue '" << this->getName() << "' could not be registered." << endl;
[8706]134            return;
135        }
136
[8858]137        orxout(internal_info, context::notifications) << "NotificationQueue '" << this->getName() << "' created." << endl;
[2911]138    }
139
140    /**
141    @brief
142        Updates the queue from time to time.
143    @param dt
144        The time interval that has passed since the last tick.
145    */
146    void NotificationQueue::tick(float dt)
147    {
[7403]148        this->tickTime_ += dt; // Add the time interval that has passed to the time counter.
[8706]149        if(this->displayTime_ != INF && this->tickTime_ >= 1.0) // If the time counter is greater than 1 s all Notifications that have expired are removed, if it is smaller we wait to the next tick.
[2910]150        {
[8706]151            this->timeLimit_.time = std::time(0)-this->displayTime_; // Container containing the current time.
[2911]152
[7403]153            std::multiset<NotificationContainer*, NotificationContainerCompare>::iterator it = this->ordering_.begin();
154            // Iterate through all elements whose creation time is smaller than the current time minus the display time.
[7410]155            while(it != this->ordering_.upper_bound(&this->timeLimit_))
[2911]156            {
[8079]157                this->remove(it); // Remove the Notifications that have expired.
158                it = this->ordering_.begin();
[2911]159            }
160
[7403]161            this->tickTime_ = this->tickTime_ - (int)this->tickTime_; // Reset time counter.
[2910]162        }
[2911]163    }
164
[8706]165    void NotificationQueue::XMLPort(Element& xmlelement, XMLPort::Mode mode)
166    {
167        SUPER(NotificationQueue, XMLPort, xmlelement, mode);
168
169        XMLPortParam(NotificationQueue, "targets", setTargets, getTargets, xmlelement, mode).defaultValues(NotificationListener::ALL);
170        XMLPortParam(NotificationQueue, "size", setMaxSize, getMaxSize, xmlelement, mode);
171        XMLPortParam(NotificationQueue, "displayTime", setDisplayTime, getDisplayTime, xmlelement, mode);
172    }
[9253]173
174
[2911]175    /**
176    @brief
[8706]177        Registers Variables to be Synchronised.
178        Registers Variables which have to be synchronised to the network system.
179      */
180    void NotificationQueue::registerVariables()
181    {
182        registerVariable( this->name_, VariableDirection::ToClient, new NetworkCallback<NotificationQueue>(this, &NotificationQueue::changedName));
183        registerVariable( this->maxSize_, VariableDirection::ToClient, new NetworkCallback<NotificationQueue>(this, &NotificationQueue::maxSizeChanged));
184        registerVariable( this->targets_, VariableDirection::ToClient, new NetworkCallback<NotificationQueue>(this, &NotificationQueue::targetsChanged));
185        registerVariable( this->displayTime_, VariableDirection::ToClient, new NetworkCallback<NotificationQueue>(this, &NotificationQueue::displayTimeChanged));
186    }
187
188    /**
189    @brief
[2911]190        Updates the NotificationQueue.
[7403]191        Updates by clearing the queue and requesting all relevant Notifications from the NotificationManager and inserting them into the queue.
[8706]192        This is called by the NotificationManager when the Notifications have changed so much, that the NotificationQueue may have to re-initialize his operations.
[2911]193    */
194    void NotificationQueue::update(void)
195    {
196        this->clear();
197
[7403]198        std::multimap<std::time_t, Notification*>* notifications = new std::multimap<std::time_t, Notification*>;
199        // Get the Notifications sent in the interval from now to now minus the display time.
[8706]200        if(this->displayTime_ == INF)
201            NotificationManager::getInstance().getNewestNotifications(this, notifications, this->getMaxSize());
202        else
203            NotificationManager::getInstance().getNotifications(this, notifications, this->displayTime_);
[2910]204
[7403]205        if(!notifications->empty())
[2911]206        {
[8706]207            // Add all Notifications that have been created after this NotificationQueue was created.
[7403]208            for(std::multimap<std::time_t, Notification*>::iterator it = notifications->begin(); it != notifications->end(); it++)
[8706]209            {
210                if(it->first >= this->creationTime_)
211                    this->push(it->second, it->first);
212            }
[2911]213        }
214
215        delete notifications;
216
[8858]217        orxout(verbose, context::notifications) << "NotificationQueue '" << this->getName() << "' updated." << endl;
[2280]218    }
[2910]219
[2911]220    /**
221    @brief
222        Updates the NotificationQueue by adding an new Notification.
223    @param notification
224        Pointer to the Notification.
225    @param time
226        The time the Notification was sent.
227    */
228    void NotificationQueue::update(Notification* notification, const std::time_t & time)
[2280]229    {
[7489]230        assert(notification);
231
[7403]232        this->push(notification, time);
[2910]233
[8858]234        orxout(verbose, context::notifications) << "NotificationQueue '" << this->getName() << "' updated. A new Notification has been added." << endl;
[2280]235    }
[2910]236
[2911]237    /**
238    @brief
[7403]239        Adds (pushes) a Notification to the NotificationQueue.
[8706]240        It inserts it into the storage containers, creates a corresponding container and pushes the notification message to the GUI.
[7403]241    @param notification
242        The Notification to be pushed.
243    @param time
244        The time when the Notification has been sent.
[2911]245    */
[7403]246    void NotificationQueue::push(Notification* notification, const std::time_t & time)
[2280]247    {
[7489]248        assert(notification);
249
[7403]250        NotificationContainer* container = new NotificationContainer;
251        container->notification = notification;
252        container->time = time;
253
254        // If the maximum size of the NotificationQueue has been reached the last (least recently added) Notification is removed.
255        if(this->getSize() >= this->getMaxSize())
256            this->pop();
257
258        this->size_++;
259
260        this->ordering_.insert(container);
261        // Insert the Notification at the begin of the list (vector, actually).
262        this->notifications_.insert(this->notifications_.begin(), container);
263
[8706]264        // Inform that a Notification was pushed.
265        this->notificationPushed(notification);
[8079]266
[8858]267        orxout(verbose_more, context::notifications) << "Notification \"" << notification->getMessage() << "\" pushed to NotificationQueue '" << this->getName() << "'" << endl;
268        orxout(internal_info, context::notifications) << "NotificationQueue \"" << this->getName() << "\": " << notification->getMessage() << endl;
[2911]269    }
[2500]270
[2911]271    /**
272    @brief
[7403]273        Removes (pops) the least recently added Notification form the NotificationQueue.
[2911]274    */
[7403]275    void NotificationQueue::pop(void)
[2911]276    {
[7403]277        NotificationContainer* container = this->notifications_.back();
[7412]278        // Get all the NotificationContainers that were sent the same time the NotificationContainer we want to pop was sent.
279        std::pair<std::multiset<NotificationContainer*, NotificationContainerCompare>::iterator, std::multiset<NotificationContainer*, NotificationContainerCompare>::iterator> iterators = this->ordering_.equal_range(container);
[8079]280
[7489]281        // Iterate through all suspects and remove the container as soon as we find it.
[7412]282        for(std::multiset<NotificationContainer*, NotificationContainerCompare>::iterator it = iterators.first; it != iterators.second; it++)
283        {
284            if(container == *it)
285            {
[8858]286                orxout(verbose_more, context::notifications) << "Notification \"" << (*it)->notification->getMessage() << "\" popped from NotificationQueue '" << this->getName() << "'" << endl;
[7412]287                this->ordering_.erase(it);
288                break;
289            }
290        }
[7403]291        this->notifications_.pop_back();
[2910]292
[7403]293        this->size_--;
294
295        delete container;
296
[8706]297        // Inform that a Notification was popped.
298        this->notificationPopped();
[2911]299    }
[2910]300
[2911]301    /**
302    @brief
[7403]303        Removes the Notification that is stored in the input NotificationContainer.
[7412]304    @param containerIterator
305        An iterator to the NotificationContainer to be removed.
[2911]306    */
[7412]307    void NotificationQueue::remove(const std::multiset<NotificationContainer*, NotificationContainerCompare>::iterator& containerIterator)
[2911]308    {
[7412]309        std::vector<NotificationContainer*>::iterator it = std::find(this->notifications_.begin(), this->notifications_.end(), *containerIterator);
[7403]310        // Get the index at which the Notification is.
311        std::vector<NotificationContainer*>::difference_type index = it - this->notifications_.begin ();
[8079]312
[8858]313        orxout(verbose_more, context::notifications) << "Notification \"" << (*it)->notification->getMessage() << "\" removed from NotificationQueue '" << this->getName() << "'" << endl;
[8079]314
[9253]315        delete *containerIterator;
316
[7412]317        this->ordering_.erase(containerIterator);
[7403]318        this->notifications_.erase(it);
[2911]319
[7403]320        this->size_--;
321
[8706]322        // TODO: index automatically cast?
323        // Inform that a Notification was removed.
324        this->notificationRemoved(index);
[2909]325    }
[2500]326
[2911]327    /**
328    @brief
[7403]329        Clears the NotificationQueue by removing all NotificationContainers.
[7488]330    @param noGraphics
[8706]331        If this is set to true the GUI is not informed of the clearing of the NotificationQueue. This is needed only internally.
[2911]332    */
[7488]333    void NotificationQueue::clear(bool noGraphics)
[2280]334    {
[8858]335        orxout(verbose, context::notifications) << "Clearing NotificationQueue " << this->getName() << "." << endl;
[7403]336        this->ordering_.clear();
337        // Delete all NotificationContainers in the list.
338        for(std::vector<NotificationContainer*>::iterator it = this->notifications_.begin(); it != this->notifications_.end(); it++)
339            delete *it;
[2911]340
[7403]341        this->notifications_.clear();
342        this->size_ = 0;
[2346]343    }
[2500]344
[2911]345    /**
346    @brief
[7403]347        Sets the name of the NotificationQueue.
348    @param name
349        The name to be set.
[2911]350    */
[7403]351    void NotificationQueue::setName(const std::string& name)
[2280]352    {
[7403]353        this->name_ = name;
[2280]354    }
[2500]355
[2911]356    /**
357    @brief
[7403]358        Sets the maximum number of displayed Notifications.
359    @param size
360        The size to be set.
[2911]361    */
[7403]362    void NotificationQueue::setMaxSize(unsigned int size)
[2280]363    {
[7403]364        if(this->maxSize_ == size)
365            return;
[2911]366
[8706]367        if(size == 0)
368        {
[8858]369            orxout(internal_warning, context::notifications) << "Trying to set maximal size of NotificationQueue '" << this->getName() << "' to 0. Ignoring..." << endl;
[8706]370            return;
371        }
[9253]372
[7403]373        this->maxSize_ = size;
[8706]374        this->maxSizeChanged();
375    }
[7403]376
[8706]377    /**
378    @brief
379        Is called when the maximum number of displayed Notifications has changed.
380    */
381    void NotificationQueue::maxSizeChanged(void)
382    {
383        if(this->isRegistered())
[7403]384            this->update();
[2911]385    }
386
387    /**
388    @brief
[7403]389        Sets the maximum number of seconds a Notification is displayed.
[2911]390    @param time
[8706]391        The number of seconds a Notification is displayed.
[2911]392    */
[8706]393    void NotificationQueue::setDisplayTime(int time)
[2911]394    {
[7403]395        if(this->displayTime_ == time)
396            return;
[2911]397
[8706]398        if(time != NotificationQueue::INF && time <= 0)
399        {
[8858]400            orxout(internal_warning, context::notifications) << "Trying to set display time of NotificationQueue '" << this->getName() << "' to non-positive value. Ignoring..." << endl;
[8706]401        }
[9253]402
[7403]403        this->displayTime_ = time;
[8706]404        this->displayTimeChanged();
405    }
[2911]406
[8706]407    /**
408    @brief
409        Is called when the maximum number of seconds a Notification is displayed has changed.
410    */
411    void NotificationQueue::displayTimeChanged(void)
412    {
413        if(this->isRegistered())
[7403]414            this->update();
[2911]415    }
416
417    /**
418    @brief
[8706]419        Produces all targets of the NotificationQueue concatenated as string, with commas (',') as separators.
[2911]420    @return
[7403]421        Returns the targets as a string.
[2911]422    */
[7403]423    const std::string& NotificationQueue::getTargets(void) const
[2911]424    {
[7403]425        std::stringstream stream;
426        bool first = true;
427        // Iterate through the set of targets.
[7417]428        for(std::set<std::string>::const_iterator it = this->targets_.begin(); it != this->targets_.end(); it++)
[7403]429        {
430            if(!first)
431                stream << ", ";
432            else
433                first = false;
434            stream << *it;
435        }
[2911]436
[7403]437        return *(new std::string(stream.str()));
[2911]438    }
439
440    /**
441    @brief
[7403]442        Sets the targets of the NotificationQueue.
443        The targets are the senders whose Notifications are displayed in this queue.
444    @param targets
[8706]445        Accepts a string of targets, each separated by commas (','), spaces are ignored.
[2911]446    */
[7403]447    void NotificationQueue::setTargets(const std::string & targets)
[2911]448    {
[7403]449        this->targets_.clear();
450
451        SubString string = SubString(targets, ",", " ", false);
452        for(unsigned int i = 0; i < string.size(); i++)
453            this->targets_.insert(string[i]);
454
[8706]455        this->targetsChanged();
456    }
457
458    /**
459    @brief
460        Is called when the NotificationQueue's targets have changed.
461    */
462    void NotificationQueue::targetsChanged(void)
463    {
464        // TODO: Why?
465        if(this->isRegistered())
[2911]466        {
[8706]467            NotificationManager::getInstance().unregisterQueue(this);
468            NotificationManager::getInstance().registerQueue(this);
[2911]469        }
470    }
471
[8706]472    /**
473    @brief
474        Pops all Notifications from the NotificationQueue.
475    @return
476        Returns true if successful, false if not.
477    */
478    bool NotificationQueue::tidy(void)
479    {
480        while(this->size_ > 0)
481            this->pop();
482        return true;
483    }
484
[2280]485}
[7403]486
Note: See TracBrowser for help on using the repository browser.