Tulip  4.1.0
Better Visualization Through Research
 All Classes Files Functions Variables Enumerations Enumerator Properties Groups Pages
Observable.h
1 /*
2  *
3  * This file is part of Tulip (www.tulip-software.org)
4  *
5  * Authors: David Auber and the Tulip development Team
6  * from LaBRI, University of Bordeaux 1 and Inria Bordeaux - Sud Ouest
7  *
8  * Tulip is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License
10  * as published by the Free Software Foundation, either version 3
11  * of the License, or (at your option) any later version.
12  *
13  * Tulip 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.
16  * See the GNU General Public License for more details.
17  *
18  */
19 
20 #ifndef OBSERVABLE_H
21 #define OBSERVABLE_H
22 
23 #include <tulip/TulipException.h>
24 #include <tulip/ForEach.h>
25 #include <tulip/vectorgraph.h>
26 #include <set>
27 
28 namespace tlp {
29 
30 class Observable;
31 //=======================================
32 /**
33  * @ingroup Observation
34  * @brief Event is the base class for all events used in the Observation mechanism.
35  *
36  * An Event is characterized by its type. The base Event class only carries informations as to the type of event, nothing specific.
37  *
38  * Event::DELETE : send directly to all Observers/Listeners, not affected by Observable::holdObservers().
39  * Event::MODIFICATION : sent to all Observers/Listeners. MODIFICATION are first sent to Observers and then to Listeners.
40  * Event::INFORMATION : sent only to Listeners.
41  * Event::INVALID : never sent, used internally for delaying events.
42  *
43  * @see Listener
44  * @see Observer
45  * @see Observable
46  **/
47 class TLP_SCOPE Event {
48  friend class Observable;
49  friend class Graph;
50  friend class PropertyInterface;
51 public:
52  enum EventType {TLP_DELETE = 0, TLP_MODIFICATION, TLP_INFORMATION, TLP_INVALID};
53  virtual ~Event();
54  Observable* sender() const;
55  Event(const Observable &sender, EventType type);
56  EventType type() const {
57  return _type;
58  }
59 private:
60  Event() {}
61  tlp::node _sender;
62  EventType _type;
63 };
64 //=======================================
65 /**
66  * @ingroup Observation
67  * @class ObservableException
68  * @brief ObservableException is the base class of all exceptions sent by the Observable/Listener/Observer system.
69  *
70  * @see Listener
71  * @see Observer
72  * @see Observable
73  **/
74 class TLP_SCOPE ObservableException : public tlp::TulipException {
75 public:
76  ObservableException(const std::string &desc):tlp::TulipException(desc) {
77  }
78 };
79 
80 /**
81  * @ingroup Observation
82  * @brief The Observable class is the base of Tulip's observation system.
83  *
84  * Each class that wishes to send or receive notifications needs to inherit from Observable.
85  *
86  * Tulip has two separate mechanisms of observation, Observers and Listeners.
87  * These two mechanisms work through the same class, the difference lies in the way an Observer of Listener is attached to
88  * the object whose events it will receive.
89  *
90  * The Listener is closer to the original pattern, where events are sent directly to the recipient.
91  * The Observer is a twist for performance purposes, it can receive the events in a delayed fashion, and cannot know
92  * if the event was just sent or was delayed.
93  *
94  * The purpose of this twist is to allow algorithms that perform a high number of modifications (e.g. creating a grid to route edges,
95  * creating multiple subgraphs with metrics or layouts) to run smoothly without overcrowding the event queue.
96  * As events are sent for every graph modification (e.g. adding a node, deleting a node, setting a value on a node), the
97  * sheer quantity of events sent by these algortithms would cause a big performance hit.
98  *
99  * This is avoided by using Observable::holdObservers().
100  * This holds all events in a queue and only sends them when Observable::unholdObservers() is called.
101  *
102  * The only exception to this mechanism is the Event::DELETE kind of event, that is never held back.
103  * Think of it as an unmaskable POSIX signal; whatever the connection to the object and the state of holdObserver, this event will get through.
104  * 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
105  * keeps a pointer to the now deleted object.
106  *
107  * The Listener however is not affected by the use of Observable::holdObservers() and Observable::unholdObservers().
108  *
109  * The observables Observers and Listeners are internally stocked in a Graph structure, allowing to visualize the connections easily.
110  * This eases debugging of Observation-related bugs.
111  *
112  * Events are always sent to Listeners first, and then to Observers, even when there is no hold.
113  *
114  * If you wish to receive events, you must inherit from Observable, and implement one of two virtual functions.
115  *
116  * If you need to received events without delay, you will be a Listener, and need to implement treatEvent().
117  * You will attach to the object you wish to receive events from using addObserver().
118  *
119  * If you can receive events after a delay, you will be an Observer, and need to implement treatEvents().
120  * You will attach to the object you wish to receive events from using addListener().
121  *
122  **/
123 class TLP_SCOPE Observable {
124  friend class Event;
125 
126 public:
127  /**
128  * @brief Holds back all events until Observable::unholdObservers() is called.
129  *
130  * Listeners are not affected by this function.
131  * Once this function is called, all events heading to an Observer will be held, except DELETE events.
132  * The events are stored in a queue, and will be sent once Observable::unholdObservers() is called.
133  *
134  * It is possible to nest calls to Observable::holdObservers() and Observable::unholdObservers(),
135  * and in this case the events will only be sent when there
136  * have been as many calls to Observable::unholdObservers() as to Observable::holdObservers().
137  *
138  * It is possible to check whether the events are being help by checking the Observable::observersHoldCounter() function.
139  *
140  * @see unholdObservers
141  * @see observersHoldCounter
142  */
143  static void holdObservers();
144  /**
145  * @brief Sends all held events to the Observers.
146  *
147  * Listeners are not affected by this function.
148  *
149  * In debug mode, if the hold counter is less than one when calling this function, an ObservableException will be raised.
150  *
151  * @see holdObservers
152  * @see observersHoldCounter
153  */
154  static void unholdObservers();
155 
156  /**
157  * @brief observersHoldCounter gives the number of times holdObservers() has been called without a matching unholdObservers() call.
158  * @return the number of times holdObservers() has been called without a matching unholdObservers() call.
159  */
160  static unsigned int observersHoldCounter() {
161  return _oHoldCounter;
162  }
163 
164  /**
165  * @brief addObserver Adds an Observer to this object.
166  *
167  * The observer will receive events sent by this object, if there is no hold applied.
168  * @param observer The object that will receive events.
169  */
170  void addObserver(Observable * const observer) const;
171 
172  /**
173  * @brief addListener Adds a Listener to this object.
174  *
175  * The Listener will receive events regardless of the state of holdObservers() and unholdObservers().
176  * @param listener The object that will receive events.
177  */
178  void addListener(Observable * const listener) const;
179 
180  /**
181  * @brief removeObserver Removes an observer from this object.
182  *
183  * The observer will no longer receive events from this object.
184  * @param observer The observer to remove from this object.
185  */
186  void removeObserver(Observable * const observerver) const;
187 
188  /**
189  * @brief removeListener Removes a listener from this object.
190  *
191  * The listener will no longer receive events from this object.
192  * @param listener The listener to remove from this object.
193  */
194  void removeListener(Observable * const listener) const;
195 
196  /**
197  * @brief getSent gets the number of sent events.
198  * @return the number of sent events
199  */
200  unsigned int getSent() const;
201  \
202  /**
203  * @brief getReceived get the number of received events.
204  * @return the number of received events.
205  */
206  unsigned int getReceived() const;
207 
208  /**
209  * @brief countObservers gets the number of observers attached to this object.
210  * @return the number of observers attached to this object.
211  */
212  unsigned int countObservers() const;
213 
214  /**
215  * @brief countListeners gets the number of listeners attached to this object.
216  * @return the number of listeners attached to this object.
217  */
218  unsigned int countListeners() const;
219 
220 public:
221  /**
222  * @brief isAlive Internal function for debugging.
223  *
224  * If there is no hold curretly applied, or no update ongoing, always returns true.
225  * Checks if the object represented by the node has been deleted.
226  * @param n The node to check for life signs.
227  * @return Whether the node is dead, Jim.
228  **/
229  static bool isAlive(tlp::node n);
230 
231  /**
232  * @brief getObject Internal function for debugging.
233  *
234  * If called during an update, it is possible the pointed object has been deleted.
235  * use isAlive() to check for this and avoid a crash.
236  * @param n The node representing the object to retrieve.
237  * @return The object represented by the node.
238  **/
239  static Observable* getObject(tlp::node n);
240 
241  /**
242  * @brief getObservableGraph Gets the observation graph.
243  * @return The graph containing a node for each Observable/Observer/Listener, and an edge between connected objects.
244  */
245  static const tlp::VectorGraph& getObservableGraph();
246 
247 protected:
248  Observable();
249  Observable(const Observable &);
250  virtual ~Observable();
251  Observable& operator=(const Observable &);
252 
253  /**
254  * @brief sendEvent Sends an event to all the Observers/Listeners.
255  * It is higly recommended to check if there are observers/listeners to send the event to before actually sending it
256  * to avoid the overhead of creating too many objects.
257  *
258  * This can be achieved by using the hasOnLookers() function :
259  *
260  * @code
261  * if (hasOnlookers()) {
262  * sendEvent(GraphEvent(*this, GraphEvent::TLP_ADD_NODE, n));
263  * }
264  * @endcode
265  *
266  * @param message The message to send to the listeners/observers.
267  */
268  void sendEvent(const Event &message);
269 
270  /**
271  * @brief treatEvents This function is called when events are sent to Observers, and Observers only.
272  *
273  * It is passed a vector of all the events that happened since the last call.
274  * 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.
275  *
276  * @param events The events that happened since the last unHoldObservers().
277  */
278  virtual void treatEvents(const std::vector<Event> &events);
279 
280  /**
281  * @brief treatEvent This function is called when events are sent to the Listeners, and Listeners only.
282  *
283  * Is it passed a reference to the event that just happened.
284  *
285  * @param message The event that was sent.
286  */
287  virtual void treatEvent(const Event &message);
288 
289  /**
290  * @brief Sends the Event::DELETE before the deletion of the subclass and its internal objects.
291  *
292  * The observation system automatically sends the DELETE event when the Observable is deleted, but
293  * in the case you need to access members of the class inheriting from Observable, you need the event
294  * sent *before* the outermost objects are destroyed.
295  *
296  * To achieve this, you can call this function in your destructor, and the DELETE event will be sent.
297  * This will allow your Listeners/Observers to access your members one last time before deletion.
298  *
299  * @warning This function must be called one time only per object.
300  * Make sure no other class in the inheritance tree calls this function before adding this call to your destructor.
301  */
302  void observableDeleted();
303 
304  /**
305  * @brief hasOnlookers Checks whether there are Observers/Listeners attached to this object.
306  *
307  * Using this function avoids the creation of events that no-one will see :
308  * @code
309  * if (hasOnlookers()) {
310  * sendEvent(GraphEvent(*this, GraphEvent::TLP_ADD_NODE, n));
311  * }
312  * @endcode
313  *
314  * @return
315  */
316  bool hasOnlookers() const;
317 
318  /**
319  * @brief use for old observer tulip compatibility
320  */
321  _DEPRECATED tlp::Iterator<tlp::Observable *> * getObservables() const;
322 
323  /**
324  * @brief use for old observer tulip compatibility
325  */
326  void _DEPRECATED notifyObservers();
327 
328 private:
329  enum OBSERVABLEEDGETYPE {OBSERVABLE = 0x01, OBSERVER = 0x02, LISTENER = 0x04};
330 
331  /**
332  * @brief deleteMsgSent This allows for calling observableDeleted() multiple times safely.
333  */
334  bool deleteMsgSent;
335 
336  /**
337  * @brief queuedEvent Used to prevent unecessary elements insertion in the set of events.
338  */
339  mutable bool queuedEvent;
340 
341  /**
342  * @brief _n The node that represents this object in the ObservableGraph.
343  */
344  tlp::node _n;
345 #ifndef NDEBUG
346  /**
347  * @brief sent The number of sent events
348  */
349  unsigned int sent;
350 
351  /**
352  * @brief received The number of received events.
353  */
354  unsigned int received;
355 #endif
356 
357  /**
358  * @brief return an Iterator on all Onlookers
359  * @warning adding or removing Onlooker to that Observable will devalidate the iterator
360  * @see StableIterator
361  * @see forEach
362  * @see stableForEach
363  */
364  tlp::Iterator<Observable *> *getOnlookers() const;
365 
366  /**
367  * @brief getInObjects Retrieves Inbound objects (observed objects; i.e. Observable).
368  * @return an iterator on 'in' objects (Observable), the iterator guarantees that all objects are alive (not deleted during hold or notify).
369  */
370  tlp::Iterator<tlp::node> *getInObjects() const;
371 
372  /**
373  * @brief getOutObjects Retrieves Outbound objects (observing objects; i.e. Listeners and Observers).
374  * @return an iterator on out objects (Listener/Observer), the iterator garantees that all objects are alive (not deleted during hold or notify).
375  */
376  tlp::Iterator<tlp::node> *getOutObjects() const;
377 
378  /**
379  * @brief addOnlooker Adds an Observer/Listener to this object.
380  *
381  * The added Onlookers will received the next Event sent by the Observable.
382  * In case of nested unholding (higly unlikely), calling that function inside hold/unhold block
383  * can make the Observer receive an event that has been sent before it was Observing the object.
384  *
385  * @param obs The Observer/Listener to add to this object.
386  * @param type The kind of observation (Listener or Observer).
387  */
388  void addOnlooker(const Observable &obs, OBSERVABLEEDGETYPE type) const;
389 
390  /**
391  * @brief removeOnlooker removes an Observer/Listener from this object.
392  *
393  * In case of nested unholding (higly unlikely), calling that function inside a hold/unhold block could
394  * make the Observer not receive an event that was sent when it was connected.
395  *
396  * @warning removing OnLooker that has been created outside of your code can create serious
397  * problems in your application. Objects that are listening/observing could need to receive
398  * the events to work properly.
399  *
400  * @param obs The Observer/Listener to remove
401  * @param type The kind of connection (Listener or Observer).
402  */
403  void removeOnlooker(const Observable &obs, OBSERVABLEEDGETYPE type) const;
404 
405  /**
406  * @brief getNode Getys the node representing this object in the ObservableGraph.
407  * @return the node representing that ObservableObject in the ObservableGraph.
408  */
409  tlp::node getNode() const;
410 
411  //static members and methods
412  /**
413  * @brief _oGraph the graph used to store all observers and connection between them.
414  */
415  static tlp::VectorGraph _oGraph;
416 
417  /**
418  * @brief _oPointer a pointer to the object represented by a node
419  */
420  static tlp::NodeProperty<Observable *> _oPointer;
421 
422  /**
423  * @brief _oAlive whether an object has been deleted or not
424  */
425  static tlp::NodeProperty<bool> _oAlive;
426 
427  /**
428  * @brief _oType the type of relation between two Observable Objects
429  */
430  static tlp::EdgeProperty<unsigned char> _oType;
431 
432  /**
433  * @brief _oDelayedDelNode store deleted nodes, to remove them at the end of the notify
434  */
435  static std::vector<tlp::node> _oDelayedDelNode;
436 
437  static std::set<std::pair<tlp::node, tlp::node> > _oDelayedEvents;
438 
439  /**
440  * @brief _oNotifying counter of nested notify calls
441  */
442  static unsigned int _oNotifying;
443 
444  /**
445  * @brief _oUnholding counter of nested unhold calls
446  */
447  static unsigned int _oUnholding;
448 
449  /**
450  * @brief _oHoldCounter counter of nested holds
451  */
452  static unsigned int _oHoldCounter;
453 
454  /**
455  * @brief _oInitialized use to initialize oGraph when the library is loaded (nice hack)
456  */
457  static bool _oInitialized;
458 
459  /**
460  * @brief delete nodes from the ObservableGraph that have been preserved to keep coherency and check bad use of the mechanism.
461  */
462  static void updateObserverGraph();
463 
464  /**
465  * @brief getBoundNode
466  * @return the bound node representing this ObservableObject in the ObservableGraph,
467  * if _n is not valid it is then bound to a new added node
468  */
469  tlp::node getBoundNode();
470  bool isBound() const {
471  return _n.isValid();
472  }
473 
474  /**
475  * @brief Trick to init the ObservableGraph properties (called at the loading of the library, during static initialization).
476  */
477  static bool init();
478 };
479 
480 }
481 
482 #endif