michael@0: // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. michael@0: // Use of this source code is governed by a BSD-style license that can be michael@0: // found in the LICENSE file. michael@0: michael@0: #ifndef BASE_OBSERVER_LIST_H__ michael@0: #define BASE_OBSERVER_LIST_H__ michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include "base/basictypes.h" michael@0: #include "base/logging.h" michael@0: michael@0: #if defined(ANDROID) && defined(_STLP_STD_NAME) michael@0: using _STLP_STD_NAME::find; michael@0: #endif michael@0: michael@0: namespace base { michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: // michael@0: // OVERVIEW: michael@0: // michael@0: // A container for a list of observers. Unlike a normal STL vector or list, michael@0: // this container can be modified during iteration without invalidating the michael@0: // iterator. So, it safely handles the case of an observer removing itself michael@0: // or other observers from the list while observers are being notified. michael@0: // michael@0: // TYPICAL USAGE: michael@0: // michael@0: // class MyWidget { michael@0: // public: michael@0: // ... michael@0: // michael@0: // class Observer { michael@0: // public: michael@0: // virtual void OnFoo(MyWidget* w) = 0; michael@0: // virtual void OnBar(MyWidget* w, int x, int y) = 0; michael@0: // }; michael@0: // michael@0: // void AddObserver(Observer* obs) { michael@0: // observer_list_.AddObserver(obs); michael@0: // } michael@0: // michael@0: // void RemoveObserver(Observer* obs) { michael@0: // observer_list_.RemoveObserver(obs); michael@0: // } michael@0: // michael@0: // void NotifyFoo() { michael@0: // FOR_EACH_OBSERVER(Observer, observer_list_, OnFoo(this)); michael@0: // } michael@0: // michael@0: // void NotifyBar(int x, int y) { michael@0: // FOR_EACH_OBSERVER(Observer, observer_list_, OnBar(this, x, y)); michael@0: // } michael@0: // michael@0: // private: michael@0: // ObserverList observer_list_; michael@0: // }; michael@0: // michael@0: // michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: template michael@0: class ObserverList { michael@0: public: michael@0: // Enumeration of which observers are notified. michael@0: enum NotificationType { michael@0: // Specifies that any observers added during notification are notified. michael@0: // This is the default type if non type is provided to the constructor. michael@0: NOTIFY_ALL, michael@0: michael@0: // Specifies that observers added while sending out notification are not michael@0: // notified. michael@0: NOTIFY_EXISTING_ONLY michael@0: }; michael@0: michael@0: ObserverList() : notify_depth_(0), type_(NOTIFY_ALL) {} michael@0: ObserverList(NotificationType type) : notify_depth_(0), type_(type) {} michael@0: ~ObserverList() { michael@0: // When check_empty is true, assert that the list is empty on destruction. michael@0: if (check_empty) { michael@0: Compact(); michael@0: DCHECK_EQ(observers_.size(), 0U); michael@0: } michael@0: } michael@0: michael@0: // Add an observer to the list. michael@0: void AddObserver(ObserverType* obs) { michael@0: DCHECK(find(observers_.begin(), observers_.end(), obs) == observers_.end()) michael@0: << "Observers can only be added once!"; michael@0: observers_.push_back(obs); michael@0: } michael@0: michael@0: // Remove an observer from the list. michael@0: void RemoveObserver(ObserverType* obs) { michael@0: typename ListType::iterator it = michael@0: std::find(observers_.begin(), observers_.end(), obs); michael@0: if (it != observers_.end()) { michael@0: if (notify_depth_) { michael@0: *it = 0; michael@0: } else { michael@0: observers_.erase(it); michael@0: } michael@0: } michael@0: } michael@0: michael@0: size_t size() const { michael@0: return observers_.size(); michael@0: } michael@0: michael@0: ObserverType* GetElementAt(int index) const { michael@0: return observers_[index]; michael@0: } michael@0: michael@0: // An iterator class that can be used to access the list of observers. See michael@0: // also the FOREACH_OBSERVER macro defined below. michael@0: class Iterator { michael@0: public: michael@0: Iterator(const ObserverList& list) michael@0: : list_(list), michael@0: index_(0), michael@0: max_index_(list.type_ == NOTIFY_ALL ? michael@0: std::numeric_limits::max() : michael@0: list.observers_.size()) { michael@0: ++list_.notify_depth_; michael@0: } michael@0: michael@0: ~Iterator() { michael@0: if (--list_.notify_depth_ == 0) michael@0: list_.Compact(); michael@0: } michael@0: michael@0: ObserverType* GetNext() { michael@0: ListType& observers = list_.observers_; michael@0: // Advance if the current element is null michael@0: size_t max_index = std::min(max_index_, observers.size()); michael@0: while (index_ < max_index && !observers[index_]) michael@0: ++index_; michael@0: return index_ < max_index ? observers[index_++] : NULL; michael@0: } michael@0: michael@0: private: michael@0: const ObserverList& list_; michael@0: size_t index_; michael@0: size_t max_index_; michael@0: }; michael@0: michael@0: private: michael@0: typedef std::vector ListType; michael@0: michael@0: void Compact() const { michael@0: typename ListType::iterator it = observers_.begin(); michael@0: while (it != observers_.end()) { michael@0: if (*it) { michael@0: ++it; michael@0: } else { michael@0: it = observers_.erase(it); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // These are marked mutable to facilitate having NotifyAll be const. michael@0: mutable ListType observers_; michael@0: mutable int notify_depth_; michael@0: NotificationType type_; michael@0: michael@0: friend class ObserverList::Iterator; michael@0: michael@0: DISALLOW_EVIL_CONSTRUCTORS(ObserverList); michael@0: }; michael@0: michael@0: } // namespace base michael@0: michael@0: #define FOR_EACH_OBSERVER(ObserverType, observer_list, func) \ michael@0: do { \ michael@0: base::ObserverList::Iterator it(observer_list); \ michael@0: ObserverType* obs; \ michael@0: while ((obs = it.GetNext()) != NULL) \ michael@0: obs->func; \ michael@0: } while (0) michael@0: michael@0: #endif // BASE_OBSERVER_LIST_H__