Tulip
4.6.0
Better Visualization Through Research
|
00001 /* 00002 * 00003 * This file is part of Tulip (www.tulip-software.org) 00004 * 00005 * Authors: David Auber and the Tulip development Team 00006 * from LaBRI, University of Bordeaux 00007 * 00008 * Tulip is free software; you can redistribute it and/or modify 00009 * it under the terms of the GNU Lesser General Public License 00010 * as published by the Free Software Foundation, either version 3 00011 * of the License, or (at your option) any later version. 00012 * 00013 * Tulip is distributed in the hope that it will be useful, 00014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 00016 * See the GNU General Public License for more details. 00017 * 00018 */ 00019 00020 #ifndef OBSERVABLE_H 00021 #define OBSERVABLE_H 00022 00023 #include <tulip/TulipException.h> 00024 #include <tulip/ForEach.h> 00025 #include <tulip/vectorgraph.h> 00026 #include <set> 00027 00028 namespace tlp { 00029 00030 class Observable; 00031 //======================================= 00032 /** 00033 * @ingroup Observation 00034 * @brief Event is the base class for all events used in the Observation mechanism. 00035 * 00036 * An Event is characterized by its type. The base Event class only carries information as to the type of event, nothing specific. 00037 * 00038 * Event::DELETE : send directly to all Observers/Listeners, not affected by Observable::holdObservers(). 00039 * Event::MODIFICATION : sent to all Observers/Listeners. MODIFICATION are first sent to Observers and then to Listeners. 00040 * Event::INFORMATION : sent only to Listeners. 00041 * Event::INVALID : never sent, used internally for delaying events. 00042 * 00043 * @see Listener 00044 * @see Observer 00045 * @see Observable 00046 **/ 00047 class TLP_SCOPE Event { 00048 friend class Observable; 00049 friend class Graph; 00050 friend class PropertyInterface; 00051 public: 00052 enum EventType {TLP_DELETE = 0, TLP_MODIFICATION, TLP_INFORMATION, TLP_INVALID}; 00053 virtual ~Event(); 00054 Observable* sender() const; 00055 Event(const Observable &sender, EventType type); 00056 EventType type() const { 00057 return _type; 00058 } 00059 private: 00060 Event() {} 00061 tlp::node _sender; 00062 EventType _type; 00063 }; 00064 //======================================= 00065 /** 00066 * @ingroup Observation 00067 * @class ObservableException 00068 * @brief ObservableException is the base class of all exceptions sent by the Observable/Listener/Observer system. 00069 * 00070 * @see Listener 00071 * @see Observer 00072 * @see Observable 00073 **/ 00074 class TLP_SCOPE ObservableException : public tlp::TulipException { 00075 public: 00076 ObservableException(const std::string &desc):tlp::TulipException(desc) { 00077 } 00078 }; 00079 00080 /** 00081 * @ingroup Observation 00082 * @brief The Observable class is the base of Tulip's observation system. 00083 * 00084 * Each class that wishes to send or receive notifications needs to inherit from Observable. 00085 * 00086 * Tulip has two separate mechanisms of observation, Observers and Listeners. 00087 * These two mechanisms work through the same class, the difference lies 00088 * in the way an Observer or a Listener is attached to the object whose events 00089 * it will receive. 00090 * 00091 * The Listener is closer to the original pattern, where events are sent directly to the recipient. 00092 * The Observer is a twist for performance purposes, it can receive the events in a delayed fashion, and cannot know if the event was just sent or was delayed. 00093 * 00094 * The purpose of this twist is to allow algorithms that perform a high number of modifications (e.g. creating a grid to route edges, 00095 * creating multiple subgraphs with metrics or layouts) to run smoothly without overcrowding the event queue. 00096 * As events are sent for every graph modification (e.g. adding a node, deleting a node, setting a value on a node), the sheer quantity of events sent by these 00097 * algorithms would cause a big performance hit. 00098 * 00099 * This can be avoided by using Observable::holdObservers(). 00100 * This holds all events in a queue and only sends them when Observable::unholdObservers() is called. 00101 * 00102 * The only exception to this mechanism is the Event::DELETE kind of event, that is never held back. 00103 * Think of it as an unmaskable POSIX signal; whatever the connection to the object and the state of holdObserver, this event will get through. 00104 * This is used to prevent crashes in the case where and object is listened or observed and is deleted, as it is likely the recipient 00105 * keeps a pointer to the now deleted object. 00106 * 00107 * The Listener however is not affected by the use of Observable::holdObservers() and Observable::unholdObservers(). 00108 * 00109 * The observable Observers and Listeners are internally store in a Graph structure, allowing to easily visualize their connections. 00110 * This eases debugging of Observation-related bugs. 00111 * 00112 * Events are always sent to Listeners first, and then to Observers, even when there is no hold. 00113 * 00114 * If you wish to receive events, you must inherit from Observable, and implement one of two virtual functions. 00115 * 00116 * If you need to received events without delay, you will be a Listener, and need to implement treatEvent(). 00117 * You will attach to the object you wish to receive events from using addListener(). 00118 * 00119 * If you can receive events after a delay, you will be an Observer, and need to implement treatEvents(). 00120 * You will attach to the object you wish to receive events from using addObserver(). 00121 * 00122 **/ 00123 class TLP_SCOPE Observable { 00124 friend class Event; 00125 00126 public: 00127 /** 00128 * @brief Holds back all events until Observable::unholdObservers() is called. 00129 * 00130 * Listeners are not affected by this function. 00131 * Once this function is called, all events heading to an Observer will be held, except DELETE events. 00132 * The events are stored in a queue, and will be sent once Observable::unholdObservers() is called. 00133 * 00134 * It is possible to nest calls to Observable::holdObservers() and Observable::unholdObservers(), 00135 * and in this case the events will only be sent when there 00136 * have been as many calls to Observable::unholdObservers() as to Observable::holdObservers(). 00137 * 00138 * It is possible to check whether the events are being help by checking the Observable::observersHoldCounter() function. 00139 * 00140 * @see unholdObservers 00141 * @see observersHoldCounter 00142 */ 00143 static void holdObservers(); 00144 /** 00145 * @brief Sends all held events to the Observers. 00146 * 00147 * Listeners are not affected by this function. 00148 * 00149 * In debug mode, if the hold counter is less than one when calling this function, an ObservableException will be raised. 00150 * 00151 * @see holdObservers 00152 * @see observersHoldCounter 00153 */ 00154 static void unholdObservers(); 00155 00156 /** 00157 * @brief observersHoldCounter gives the number of times holdObservers() has been called without a matching unholdObservers() call. 00158 * @return the number of times holdObservers() has been called without a matching unholdObservers() call. 00159 */ 00160 static unsigned int observersHoldCounter() { 00161 return _oHoldCounter; 00162 } 00163 00164 /** 00165 * @brief Adds an Observer to this object. 00166 * 00167 * The observer will receive events sent by this object, if there is no hold applied. 00168 * @param observer The object that will receive events. 00169 */ 00170 void addObserver(Observable * const observer) const; 00171 00172 /** 00173 * @brief Adds a Listener to this object. 00174 * 00175 * The Listener will receive events regardless of the state of holdObservers() and unholdObservers(). 00176 * @param listener The object that will receive events. 00177 */ 00178 void addListener(Observable * const listener) const; 00179 00180 /** 00181 * @brief Removes an observer from this object. 00182 * 00183 * The observer will no longer receive events from this object. 00184 * @param observer The observer to remove from this object. 00185 */ 00186 void removeObserver(Observable * const observerver) const; 00187 00188 /** 00189 * @brief Removes a listener from this object. 00190 * 00191 * The listener will no longer receive events from this object. 00192 * @param listener The listener to remove from this object. 00193 */ 00194 void removeListener(Observable * const listener) const; 00195 00196 /** 00197 * @brief gets the number of sent events. 00198 * @return the number of sent events (0 when compiling with -DNDEBUG). 00199 */ 00200 unsigned int getSent() const; 00201 \ 00202 /** 00203 * @brief get the number of received events. 00204 * @return the number of received events (0 when compiling with -DNDEBUG). 00205 */ 00206 unsigned int getReceived() const; 00207 00208 /** 00209 * @brief gets the number of observers attached to this object. 00210 * @return the number of observers attached to this object. 00211 */ 00212 unsigned int countObservers() const; 00213 00214 /** 00215 * @brief gets the number of listeners attached to this object. 00216 * @return the number of listeners attached to this object. 00217 */ 00218 unsigned int countListeners() const; 00219 00220 public: 00221 /** 00222 * @brief internal function for debugging purpose 00223 * If there is no hold currently applied, or no update ongoing, always returns true. 00224 * Checks if the object represented by the node has been deleted. 00225 * @param n The node to check for life signs. 00226 * @return Whether the node is dead. 00227 **/ 00228 static bool isAlive(tlp::node n); 00229 00230 /** 00231 * @brief internal function for debugging purpose 00232 * If called during an update, it is possible the pointed object has been deleted. 00233 * use isAlive() to check for this and avoid a crash. 00234 * @param n The node representing the object to retrieve. 00235 * @return The object represented by the node. 00236 **/ 00237 static Observable* getObject(tlp::node n); 00238 00239 /** 00240 * @brief internal function for debugging purpose 00241 * getScheduled get the number of scheduled events for this Observable. 00242 * @param n The node of an Observable object 00243 * @return the number of scheduled events involving this Observable as sender or receiver. 00244 */ 00245 static unsigned int getScheduled(tlp::node n); 00246 00247 /** 00248 * @brief internal function for debugging purpose 00249 * Get the node representing this object in the Observable Graph. 00250 * @param obs the Observable object 00251 * @return the node of the Observable object in the Observable Graph. 00252 */ 00253 static tlp::node getNode(const tlp::Observable* obs); 00254 00255 /** 00256 * @brief Gets the observation graph. 00257 * @return The graph containing a node for each Observable/Observer/Listener, and an edge between connected objects. 00258 */ 00259 static const tlp::VectorGraph& getObservableGraph(); 00260 00261 protected: 00262 Observable(); 00263 Observable(const Observable &); 00264 virtual ~Observable(); 00265 Observable& operator=(const Observable &); 00266 00267 /** 00268 * @brief Sends an event to all the Observers/Listeners. 00269 * It is higly recommended to check if there are observers/listeners to send the event to before actually sending it 00270 * to avoid the overhead of creating too many objects. 00271 * 00272 * This can be achieved by using the hasOnLookers() function : 00273 * 00274 * @code 00275 * if (hasOnlookers()) { 00276 * sendEvent(GraphEvent(*this, GraphEvent::TLP_ADD_NODE, n)); 00277 * } 00278 * @endcode 00279 * 00280 * @param message The message to send to the listeners/observers. 00281 */ 00282 void sendEvent(const Event &message); 00283 00284 /** 00285 * @brief This function is called when events are sent to Observers, and Observers only. 00286 * 00287 * It is passed a vector of all the events that happened since the last call. 00288 * If events were held, this vector can be pretty large, and if events were not held it is likely it only contains a single element. 00289 * 00290 * @param events The events that happened since the last unHoldObservers(). 00291 */ 00292 virtual void treatEvents(const std::vector<Event> &events); 00293 00294 /** 00295 * @brief This function is called when events are sent to the Listeners, and Listeners only. 00296 * 00297 * Is it passed a reference to the event that just happened. 00298 * 00299 * @param message The event that was sent. 00300 */ 00301 virtual void treatEvent(const Event &message); 00302 00303 /** 00304 * @brief Sends the Event::DELETE before the deletion of the subclass and its internal objects. 00305 * 00306 * The observation system automatically sends the DELETE event when the Observable is deleted, but 00307 * in the case you need to access members of the class inheriting from Observable, you need the event 00308 * sent *before* the outermost objects are destroyed. 00309 * 00310 * To achieve this, you can call this function in your destructor, and the DELETE event will be sent. 00311 * This will allow your Listeners/Observers to access your members one last time before deletion. 00312 * 00313 * @warning This function must be called only once per object. 00314 * Make sure no other class in the inheritance tree calls this function before adding this call to your destructor. 00315 */ 00316 void observableDeleted(); 00317 00318 /** 00319 * @brief Checks whether there are Observers/Listeners attached to this object. 00320 * 00321 * Using this function avoids the creation of events that no-one will see : 00322 * @code 00323 * if (hasOnlookers()) { 00324 * sendEvent(GraphEvent(*this, GraphEvent::TLP_ADD_NODE, n)); 00325 * } 00326 * @endcode 00327 * 00328 * @return 00329 */ 00330 bool hasOnlookers() const; 00331 00332 /** 00333 * @brief use for old observer tulip compatibility 00334 */ 00335 _DEPRECATED tlp::Iterator<tlp::Observable *> * getObservables() const; 00336 00337 /** 00338 * @brief use for old observer tulip compatibility 00339 */ 00340 _DEPRECATED void notifyObservers(); 00341 00342 private: 00343 enum OBSERVABLEEDGETYPE {OBSERVABLE = 0x01, OBSERVER = 0x02, LISTENER = 0x04}; 00344 00345 /** 00346 * @brief deleteMsgSent This allows for calling observableDeleted() multiple times safely. 00347 */ 00348 bool deleteMsgSent; 00349 00350 /** 00351 * @brief queuedEvent Used to prevent unecessary elements insertion in the set of events. 00352 */ 00353 mutable bool queuedEvent; 00354 00355 /** 00356 * @brief _n The node that represents this object in the ObservableGraph. 00357 */ 00358 tlp::node _n; 00359 #ifndef NDEBUG 00360 /** 00361 * @brief sent The number of sent events 00362 */ 00363 unsigned int sent; 00364 00365 /** 00366 * @brief received The number of received events. 00367 */ 00368 unsigned int received; 00369 #endif 00370 00371 /** 00372 * @brief return an Iterator on all Onlookers 00373 * @warning adding or removing Onlooker to that Observable will devalidate the iterator 00374 * @see StableIterator 00375 * @see forEach 00376 * @see stableForEach 00377 */ 00378 tlp::Iterator<Observable *> *getOnlookers() const; 00379 00380 /** 00381 * @brief getInObjects Retrieves Inbound objects (observed objects; i.e. Observable). 00382 * @return an iterator on 'in' objects (Observable), the iterator guarantees that all objects are alive (not deleted during hold or notify). 00383 */ 00384 tlp::Iterator<tlp::node> *getInObjects() const; 00385 00386 /** 00387 * @brief getOutObjects Retrieves Outbound objects (observing objects; i.e. Listeners and Observers). 00388 * @return an iterator on out objects (Listener/Observer), the iterator garantees that all objects are alive (not deleted during hold or notify). 00389 */ 00390 tlp::Iterator<tlp::node> *getOutObjects() const; 00391 00392 /** 00393 * @brief addOnlooker Adds an Observer/Listener to this object. 00394 * 00395 * The added Onlookers will received the next Event sent by the Observable. 00396 * In case of nested unholding (higly unlikely), calling that function inside hold/unhold block 00397 * can make the Observer receive an event that has been sent before it was Observing the object. 00398 * 00399 * @param obs The Observer/Listener to add to this object. 00400 * @param type The kind of observation (Listener or Observer). 00401 */ 00402 void addOnlooker(const Observable &obs, OBSERVABLEEDGETYPE type) const; 00403 00404 /** 00405 * @brief removeOnlooker removes an Observer/Listener from this object. 00406 * 00407 * In case of nested unholding (higly unlikely), calling that function inside a hold/unhold block could 00408 * make the Observer not receive an event that was sent when it was connected. 00409 * 00410 * @warning removing OnLooker that has been created outside of your code can create serious 00411 * problems in your application. Objects that are listening/observing could need to receive 00412 * the events to work properly. 00413 * 00414 * @param obs The Observer/Listener to remove 00415 * @param type The kind of connection (Listener or Observer). 00416 */ 00417 void removeOnlooker(const Observable &obs, OBSERVABLEEDGETYPE type) const; 00418 00419 /** 00420 * @brief getNode Get the node representing this object in the ObservableGraph. 00421 * @return the node representing that ObservableObject in the ObservableGraph. 00422 */ 00423 tlp::node getNode() const; 00424 00425 //static members and methods 00426 /** 00427 * @brief _oGraph the graph used to store all observers and connection between them. 00428 */ 00429 static tlp::VectorGraph _oGraph; 00430 00431 /** 00432 * @brief _oPointer a pointer to the object represented by a node 00433 */ 00434 static tlp::NodeProperty<Observable *> _oPointer; 00435 00436 /** 00437 * @brief _oAlive whether an object has been deleted or not 00438 */ 00439 static tlp::NodeProperty<bool> _oAlive; 00440 00441 /** 00442 * @brief _oEventsToTreat the count of events scheduled to be treated by an object 00443 * the object's associated node is deleted only when this count is null 00444 * in order to prevent the node reuse and ensure the _oAlive associated value 00445 */ 00446 static tlp::NodeProperty<unsigned int> _oEventsToTreat; 00447 00448 /** 00449 * @brief _oType the type of relation between two Observable Objects 00450 */ 00451 static tlp::EdgeProperty<unsigned char> _oType; 00452 00453 /** 00454 * @brief _oDelayedDelNode store deleted nodes, to remove them at the end of the notify 00455 */ 00456 static std::vector<tlp::node> _oDelayedDelNode; 00457 00458 static std::set<std::pair<tlp::node, tlp::node> > _oDelayedEvents; 00459 00460 /** 00461 * @brief _oNotifying counter of nested notify calls 00462 */ 00463 static unsigned int _oNotifying; 00464 00465 /** 00466 * @brief _oUnholding counter of nested unhold calls 00467 */ 00468 static unsigned int _oUnholding; 00469 00470 /** 00471 * @brief _oHoldCounter counter of nested holds 00472 */ 00473 static unsigned int _oHoldCounter; 00474 00475 /** 00476 * @brief _oInitialized use to initialize oGraph when the library is loaded (nice hack) 00477 */ 00478 static bool _oInitialized; 00479 00480 /** 00481 * @brief delete nodes from the ObservableGraph that have been preserved to keep coherency and check bad use of the mechanism. 00482 */ 00483 static void updateObserverGraph(); 00484 00485 /** 00486 * @brief getBoundNode 00487 * @return the bound node representing this ObservableObject in the ObservableGraph, 00488 * if _n is not valid it is then bound to a new added node 00489 */ 00490 tlp::node getBoundNode(); 00491 bool isBound() const { 00492 return _n.isValid(); 00493 } 00494 00495 /** 00496 * @brief Trick to init the ObservableGraph properties (called at the loading of the library, during static initialization). 00497 */ 00498 static bool init(); 00499 }; 00500 00501 /** 00502 * @brief The ObserverHolder class is a convenience class to automatically hold and unhold observers. 00503 * It performs a call to Observable::holdObserver() at its creation and a call to Observable::unholdObserver() at its destruction. 00504 * You can use it if you have to hold observers in a function with multiple return points to avoid to call Observable::unholdObserver() for each of them. 00505 * @code 00506 * void myFunc(){ 00507 * ObserverHolder holder;//Automatically call Observable::holdObserver() 00508 * 00509 * if(someTest()){ 00510 * someOperation1(); 00511 * return;//No need to call Observable::unholdObserver() it will be called with the destruction of the locker object 00512 * } 00513 * 00514 * } 00515 * @endcode 00516 * 00517 */ 00518 class TLP_SCOPE ObserverHolder { 00519 public : 00520 ObserverHolder() { 00521 Observable::holdObservers(); 00522 } 00523 ~ObserverHolder() { 00524 Observable::unholdObservers(); 00525 } 00526 }; 00527 00528 // deprecated name of this class 00529 _DEPRECATED_TYPEDEF(ObserverHolder, ObserverLocker); 00530 00531 } 00532 00533 #endif