/* * Observer.cpp * * Created on: Jan 19, 2010 * Author: crueger */ #include "Observer.hpp" #include #include "Helpers/Assert.hpp" using namespace std; /****************** Static stuff for the observer mechanism ************/ // All infrastructure for the observer-pattern is bundled at a central place // this is more efficient if many objects can be observed (inherit from observable) // but only few are actually coupled with observers. E.g. TMV has over 500.000 Atoms, // which might become observable. Handling Observerable infrastructure in each of // these would use memory for each atom. By handling Observer-infrastructure // here we only need memory for objects that actually are observed. // See [Gamma et al, 1995] p. 297 map Observable::depth; //!< Map of Observables to the depth of the DAG of Observers map > Observable::callTable; //!< Table for each Observable of all its Observers std::map > Observable::notifications; set Observable::busyObservables; //!< Set of Observables that are currently busy notifying their sign-on'ed Observers /** Attaching Sub-observables to Observables. * Increases entry in Observable::depth for this \a *publisher by one. * * The two functions \sa start_observer_internal() and \sa finish_observer_internal() * have to be used together at all time. Never use these functions directly * START_OBSERVER and FINISH_OBSERVER also construct a bogus while(0) loop * thus producing compiler-errors whenever only one is used. * \param *publisher reference of sub-observable */ void Observable::start_observer_internal(Observable *publisher){ // increase the count for this observable by one // if no entry for this observable is found, an new one is created // by the STL and initialized to 0 (see STL documentation) depth[publisher]++; } /** Detaching Sub-observables from Observables. * Decreases entry in Observable::depth for this \a *publisher by one. If zero, we * start notifying all our Observers. * * The two functions start_observer_internal() and finish_observer_internal() * have to be used together at all time. Never use these functions directly * START_OBSERVER and FINISH_OBSERVER also construct a bogus while(0) loop * thus producing compiler-errors whenever only one is used. * \param *publisher reference of sub-observable */ void Observable::finish_observer_internal(Observable *publisher){ // decrease the count for this observable // if zero is reached all observed blocks are done and we can // start to notify our observers if(--(depth[publisher])){} else{ publisher->notifyAll(); // this item is done, so we don't have to keep the count with us // save some memory by erasing it depth.erase(publisher); } } void Observable::enque_notification_internal(Observable *publisher, Notification_ptr notification){ ASSERT(notification->owner==publisher,"Some object tried to send a notification it does not own"); notifications[publisher].insert(notification); } /** Constructor for Observable Protector. * Basically, calls start_observer_internal(). Hence use this class instead of * calling the function directly. * * \param *protege Observable to be protected. */ Observable::_Observable_protector::_Observable_protector(Observable *_protege) : protege(_protege) { start_observer_internal(protege); } /** Destructor for Observable Protector. * Basically, calls finish_observer_internal(). Hence use this class instead of * calling the function directly. * * \param *protege Observable to be protected. */ Observable::_Observable_protector::~_Observable_protector() { finish_observer_internal(protege); } /************* Notification mechanism for observables **************/ /** Notify all Observers of changes. * Puts \a *this into Observable::busyObservables, calls Observer::update() for all in callee_t * and removes from busy list. */ void Observable::notifyAll() { // we are busy notifying others right now // add ourselves to the list of busy subjects to enable circle detection busyObservables.insert(this); // see if anyone has signed up for observation // and call all observers try { if(callTable.count(this)) { // elements are stored sorted by keys in the multimap // so iterating over it gives us a the callees sorted by // the priorities callees_t callees = callTable[this]; callees_t::iterator iter; for(iter=callees.begin();iter!=callees.end();++iter){ (*iter).second->update(this); } } } ASSERT_NOCATCH("Exception thrown from Observer Update"); // send out all notifications that need to be done notificationSet currentNotifications = notifications[this]; for(notificationSet::iterator it = currentNotifications.begin(); it != currentNotifications.end();++it){ (*it)->notifyAll(); } notifications.erase(this); // done with notification, we can leave the set of busy subjects busyObservables.erase(this); } /** Handles passing on updates from sub-Observables. * Mimicks basically the Observer::update() function. * * \param *publisher The \a *this we observe. */ void Observable::update(Observable *publisher) { // circle detection if(busyObservables.find(this)!=busyObservables.end()) { // somehow a circle was introduced... we were busy notifying our // observers, but still we are called by one of our sub-Observables // we cannot be sure observation will still work at this point ASSERT(0,"Circle detected in observation-graph.\n" "Observation-graph always needs to be a DAG to work correctly!\n" "Please check your observation code and fix this!\n"); return; } else { // see if we are in the process of changing ourselves // if we are changing ourselves at the same time our sub-observables change // we do not need to publish all the changes at each time we are called if(depth.find(this)==depth.end()) { notifyAll(); } } } /** Sign on an Observer to this Observable. * Puts \a *target into Observable::callTable list. * \param *target Observer * \param priority number in [-20,20] */ void Observable::signOn(Observer *target,int priority) { ASSERT(priority>=-20 && priority<=+20, "Priority out of range [-20:+20] when signing on Observer"); bool res = false; callees_t &callees = callTable[this]; callees_t::iterator iter; for(iter=callees.begin();iter!=callees.end();++iter){ res |= ((*iter).second == target); } if(!res) callees.insert(pair(priority,target)); } /** Sign off an Observer from this Observable. * Removes \a *target from Observable::callTable list. * \param *target Observer */ void Observable::signOff(Observer *target) { ASSERT(callTable.count(this),"SignOff called for an Observable without Observers."); callees_t &callees = callTable[this]; callees_t::iterator iter; callees_t::iterator deliter; for(iter=callees.begin();iter!=callees.end();) { if((*iter).second == target) { callees.erase(iter++); } else { ++iter; } } if(callees.empty()){ callTable.erase(this); } } void Observable::signOn(Observer *target, Notification_ptr notification){ ASSERT(notification->owner==this, "Trying to sign on for a notification that is not provided by this object"); notification->addObserver(target); } void Observable::signOff(Observer *target, Notification_ptr notification){ ASSERT(notification->owner==this, "Trying to sign off from a notification that is not provided by this object"); notification->removeObserver(target); } bool Observable::isBlocked(){ return depth.count(this) > 0; } /** Handles sub-observables that just got killed * when an sub-observerable dies we usually don't need to do anything * \param *publisher Sub-Observable. */ void Observable::subjectKilled(Observable *publisher){ } /** Constructor for class Observable. */ Observable::Observable() {} /** Destructor for class Observable. * When an observable is deleted, we let all our observers know. \sa Observable::subjectKilled(). */ Observable::~Observable() { if(callTable.count(this)) { // delete all entries for this observable callees_t callees = callTable[this]; callees_t::iterator iter; for(iter=callees.begin();iter!=callees.end();++iter){ (*iter).second->subjectKilled(this); } callTable.erase(this); } } /** Constructor for class Observer. */ Observer::Observer() {} /** Destructor for class Observer. */ Observer::~Observer() {} /** * Method for specialized notifications. * Most Observers wont need or use this, so it is implemented * empty in the base case; */ void Observer::recieveNotification(Observable *publisher, Notification_ptr notification){ ASSERT(0,"Notification received by object that did not sign on for it."); } Notification::Notification(Observable *_owner) : owner(_owner) {} Notification::~Notification(){} void Notification::addObserver(Observer *target){ targets.insert(target); } void Notification::removeObserver(Observer *target){ targets.erase(target); } void Notification::notifyAll(){ for(std::set::iterator it=targets.begin(); it!=targets.end();++it){ (*it)->recieveNotification(owner,this); } }