source: src/Patterns/Observer/Relay.cpp@ b760ac5

Last change on this file since b760ac5 was b760ac5, checked in by Frederik Heber <heber@…>, 14 years ago

Relay can now also relay notifications.

  • NOTE: We do not yet notify the Observables, whose update() we combine, when the Relay is destroyed. They have to signOff before by themselves (or by some other means, e.g. be destroyed before.).
  • Property mode set to 100644
File size: 7.3 KB
Line 
1/*
2 * Project: MoleCuilder
3 * Description: creates and alters molecular systems
4 * Copyright (C) 2010 University of Bonn. All rights reserved.
5 * Please see the LICENSE file or "Copyright notice" in builder.cpp for details.
6 */
7
8/*
9 * Relay.cpp
10 *
11 * Created on: Dec 1, 2011
12 * Author: heber
13 */
14
15// include config.h
16#ifdef HAVE_CONFIG_H
17#include <config.h>
18#endif
19
20#include "MemDebug.hpp"
21
22#include "Relay.hpp"
23
24#include "Assert.hpp"
25#include "Channels.hpp"
26#include "Notification.hpp"
27
28
29/** Constructor for class Relay.
30 */
31Relay::Relay(std::string name) :
32 Observable(name),
33 Updater(NULL)
34{
35#ifdef LOG_OBSERVER
36 observerLog().addName(this,name);
37 observerLog().addMessage() << "++ Creating Relay " << observerLog().getName(this);
38#endif
39}
40
41/** Destructor for class Relay.
42 * When an observable is deleted, we let all our observers know. \sa Relay::subjectKilled().
43 */
44Relay::~Relay()
45{
46#ifdef LOG_OBSERVER
47 observerLog().addMessage() << "-- Destroying Relay " << observerLog().getName(this);
48#endif
49 // killing subjects is done by Observables' dstor
50}
51
52
53
54/** Sign on an Observer to this Observable.
55 * Puts \a *target into Observable::callTable list.
56 * \param *target Observer
57 * \param priority number in [-20,20]
58 */
59void Relay::signOn(Observer *target, PriorityLevel priority) const
60{
61#ifdef LOG_OBSERVER
62 observerLog().addMessage() << "@@ Signing on " << observerLog().getName(target)
63 << " to "
64 << observerLog().getName(const_cast<Observable *>(static_cast<const Observable * const>(this)));
65#endif
66 bool res = false;
67 callees_t &callees = callTable[const_cast<Observable *>(static_cast<const Observable * const>(this))];
68
69 callees_t::iterator iter;
70 for(iter=callees.begin();iter!=callees.end();++iter){
71 res |= ((*iter).second == target);
72 }
73 if(!res)
74 callees.insert(std::pair<int,Observer*>(priority.level,target));
75}
76
77/** Sign off an Observer from this Observable.
78 * Removes \a *target from Observable::callTable list.
79 * \param *target Observer
80 */
81void Relay::signOff(Observer *target) const
82{
83 ASSERT(callTable.count(const_cast<Observable *>(static_cast<const Observable * const>(this))),
84 "Relay::signOff() - called for an Observable without Observers.");
85#ifdef LOG_OBSERVER
86 observerLog().addMessage() << "** Signing off " << observerLog().getName(target)
87 << " from "
88 << observerLog().getName(const_cast<Observable *>(static_cast<const Observable * const>(this)));
89#endif
90 callees_t &callees = callTable[const_cast<Observable *>(static_cast<const Observable * const>(this))];
91
92 callees_t::iterator iter;
93 callees_t::iterator deliter;
94 for(iter=callees.begin();iter!=callees.end();) {
95 if((*iter).second == target) {
96 callees.erase(iter++);
97 }
98 else {
99 ++iter;
100 }
101 }
102 if(callees.empty()){
103 callTable.erase(const_cast<Observable *>(static_cast<const Observable * const>(this)));
104 }
105}
106
107void Relay::signOn(Observer *target, size_t channelno) const
108{
109 Notification_ptr notification = getChannel(channelno);
110 notification->addObserver(target);
111}
112
113void Relay::signOff(Observer *target, size_t channelno) const
114{
115 Notification_ptr notification = getChannel(channelno);
116 notification->removeObserver(target);
117}
118
119/** Notify all Observers of changes.
120 * Puts \a *this into Relay::busyObservables, calls Observer::update() for all in callee_t
121 * and removes from busy list.
122 */
123void Relay::notifyAll() {
124 ASSERT(Updater != NULL,
125 "Relay::notifyAll() called while Updater is NULL.");
126 // we are busy notifying others right now
127 // add ourselves to the list of busy subjects to enable circle detection
128 busyObservables.insert(this);
129 // see if anyone has signed up for observation
130 // and call all observers
131 try {
132 if(callTable.count(this)) {
133 // elements are stored sorted by keys in the multimap
134 // so iterating over it gives us a the callees sorted by
135 // the priorities
136 callees_t callees = callTable[this];
137 callees_t::iterator iter;
138 for(iter=callees.begin();iter!=callees.end();++iter){
139#ifdef LOG_OBSERVER
140 observerLog().addMessage() << "-> " << observerLog().getName(this)
141 << " is relaying update from " << observerLog().getName(Updater)
142 << " to " << observerLog().getName((*iter).second)
143 << " (priority=" << (*iter).first << ")";
144#endif
145 (*iter).second->update(Updater);
146 }
147 }
148 }
149 ASSERT_NOCATCH("Exception thrown from Observer Update");
150
151 // send out all notifications that need to be done
152
153 notificationSet currentNotifications = notifications[Updater];
154 for(notificationSet::iterator it = currentNotifications.begin();
155 it != currentNotifications.end();++it){
156 (*it)->notifyAll(Updater);
157 }
158
159 notifications.erase(Updater);
160
161 // done with notification, we can leave the set of busy subjects
162 busyObservables.erase(this);
163}
164
165
166/** Handles passing on updates from sub-Relays.
167 * Mimicks basically the Observer::update() function.
168 *
169 * \param *publisher The \a *this we observe.
170 */
171void Relay::update(Observable *publisher) {
172 // circle detection
173 if(busyObservables.find(this)!=busyObservables.end()) {
174 // somehow a circle was introduced... we were busy notifying our
175 // observers, but still we are called by one of our sub-Relays
176 // we cannot be sure observation will still work at this point
177 ASSERT(0,"Circle detected in observation-graph.\n"
178 "Observation-graph always needs to be a DAG to work correctly!\n"
179 "Please check your observation code and fix this!\n");
180 return;
181 }
182 else {
183 // see if we are in the process of changing ourselves
184 // if we are changing ourselves at the same time our sub-observables change
185 // we do not need to publish all the changes at each time we are called
186 if(depth.find(this)==depth.end()) {
187#ifdef LOG_OBSERVER
188 observerLog().addMessage() << "-* Update from " << observerLog().getName(publisher)
189 << " relayed by " << observerLog().getName(this);
190#endif
191 Updater = publisher;
192 notifyAll();
193 Updater = NULL;
194 }
195 else{
196#ifdef LOG_OBSERVER
197 observerLog().addMessage() << "-| Update from " << observerLog().getName(publisher)
198 << " not relayed by " << observerLog().getName(this);
199#endif
200 }
201 }
202}
203
204/** Method for receiving specialized notifications.
205 *
206 * \param *publisher The \a *this we observe.
207 * \param notification type of notification
208 */
209void Relay::recieveNotification(Observable *publisher, Notification_ptr notification)
210{
211 Updater = publisher;
212 const Channels *myChannels = NotificationChannels[const_cast<Observable *>(static_cast<const Observable * const>(this))];
213 ASSERT(myChannels != NULL,
214 "Relay::recieveNotification() - this relay does not have any channels.");
215 const size_t channelno = notification->getChannelNo();
216 Notification_ptr mynotification = myChannels->getChannel(channelno);
217 ASSERT(mynotification != NULL,
218 "Relay::recieveNotification() - this relay does not have a notification no "+toString(channelno)+".");
219 mynotification->notifyAll(Updater);
220 Updater = NULL;
221}
222
223/** Handles sub-observables that just got killed
224 * when an sub-observerable dies we usually don't need to do anything
225 * \param *publisher Sub-Relay.
226 */
227void Relay::subjectKilled(Observable *publisher){
228}
229
Note: See TracBrowser for help on using the repository browser.