mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-29 15:52:07 +00:00
393 lines
14 KiB
C++
393 lines
14 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is Mozilla.org code.
|
|
*
|
|
* The Initial Developer of the Original Code is Mozilla Foundation.
|
|
* Portions created by the Initial Developer are Copyright (C) 2006
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Jonas Sicking <jonas@sicking.cc> (Original Author)
|
|
* Daniel Witte <dwitte@stanford.edu>
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this file under
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
#ifndef nsTObserverArray_h___
|
|
#define nsTObserverArray_h___
|
|
|
|
#include "nsTArray.h"
|
|
|
|
/**
|
|
* An array of observers. Like a normal array, but supports iterators that are
|
|
* stable even if the array is modified during iteration.
|
|
* The template parameter T is the observer type the array will hold;
|
|
* N is the number of built-in storage slots that come with the array.
|
|
* NOTE: You probably want to use nsTObserverArray, unless you specifically
|
|
* want built-in storage. See below.
|
|
* @see nsTObserverArray, nsTArray
|
|
*/
|
|
|
|
class NS_COM_GLUE nsTObserverArray_base {
|
|
public:
|
|
typedef PRUint32 index_type;
|
|
typedef PRUint32 size_type;
|
|
typedef PRInt32 diff_type;
|
|
|
|
protected:
|
|
class Iterator_base {
|
|
protected:
|
|
friend class nsTObserverArray_base;
|
|
|
|
Iterator_base(index_type aPosition, Iterator_base* aNext)
|
|
: mPosition(aPosition),
|
|
mNext(aNext) {
|
|
}
|
|
|
|
// The current position of the iterator. Its exact meaning differs
|
|
// depending on iterator. See nsTObserverArray<T>::ForwardIterator.
|
|
index_type mPosition;
|
|
|
|
// The next iterator currently iterating the same array
|
|
Iterator_base* mNext;
|
|
};
|
|
|
|
nsTObserverArray_base()
|
|
: mIterators(nsnull) {
|
|
}
|
|
|
|
~nsTObserverArray_base() {
|
|
NS_ASSERTION(mIterators == nsnull, "iterators outlasting array");
|
|
}
|
|
|
|
/**
|
|
* Adjusts iterators after an element has been inserted or removed
|
|
* from the array.
|
|
* @param modPos Position where elements were added or removed.
|
|
* @param adjustment -1 if an element was removed, 1 if an element was
|
|
* added.
|
|
*/
|
|
void AdjustIterators(index_type aModPos, diff_type aAdjustment);
|
|
|
|
/**
|
|
* Clears iterators when the array is destroyed.
|
|
*/
|
|
void ClearIterators();
|
|
|
|
mutable Iterator_base* mIterators;
|
|
};
|
|
|
|
template<class T, PRUint32 N>
|
|
class nsAutoTObserverArray : protected nsTObserverArray_base {
|
|
public:
|
|
typedef T elem_type;
|
|
typedef nsTArray<T> array_type;
|
|
|
|
nsAutoTObserverArray() {
|
|
}
|
|
|
|
//
|
|
// Accessor methods
|
|
//
|
|
|
|
// @return The number of elements in the array.
|
|
size_type Length() const {
|
|
return mArray.Length();
|
|
}
|
|
|
|
// @return True if the array is empty or false otherwise.
|
|
PRBool IsEmpty() const {
|
|
return mArray.IsEmpty();
|
|
}
|
|
|
|
// This method provides direct access to the i'th element of the array.
|
|
// The given index must be within the array bounds.
|
|
// @param i The index of an element in the array.
|
|
// @return A reference to the i'th element of the array.
|
|
elem_type& ElementAt(index_type i) {
|
|
return mArray.ElementAt(i);
|
|
}
|
|
|
|
// Same as above, but readonly.
|
|
const elem_type& ElementAt(index_type i) const {
|
|
return mArray.ElementAt(i);
|
|
}
|
|
|
|
// This method provides direct access to the i'th element of the array in
|
|
// a bounds safe manner. If the requested index is out of bounds the
|
|
// provided default value is returned.
|
|
// @param i The index of an element in the array.
|
|
// @param def The value to return if the index is out of bounds.
|
|
elem_type& SafeElementAt(index_type i, elem_type& def) {
|
|
return mArray.SafeElementAt(i, def);
|
|
}
|
|
|
|
// Same as above, but readonly.
|
|
const elem_type& SafeElementAt(index_type i, const elem_type& def) const {
|
|
return mArray.SafeElementAt(i, def);
|
|
}
|
|
|
|
//
|
|
// Search methods
|
|
//
|
|
|
|
// This method searches, starting from the beginning of the array,
|
|
// for the first element in this array that is equal to the given element.
|
|
// 'operator==' must be defined for elem_type.
|
|
// @param item The item to search for.
|
|
// @return PR_TRUE if the element was found.
|
|
template<class Item>
|
|
PRBool Contains(const Item& item) const {
|
|
return IndexOf(item) != array_type::NoIndex;
|
|
}
|
|
|
|
// This method searches for the offset of the first element in this
|
|
// array that is equal to the given element.
|
|
// 'operator==' must be defined for elem_type.
|
|
// @param item The item to search for.
|
|
// @param start The index to start from.
|
|
// @return The index of the found element or NoIndex if not found.
|
|
template<class Item>
|
|
index_type IndexOf(const Item& item, index_type start = 0) const {
|
|
return mArray.IndexOf(item, start);
|
|
}
|
|
|
|
//
|
|
// Mutation methods
|
|
//
|
|
|
|
// Prepend an element to the array unless it already exists in the array.
|
|
// 'operator==' must be defined for elem_type.
|
|
// @param item The item to prepend.
|
|
// @return PR_TRUE if the element was found, or inserted successfully.
|
|
template<class Item>
|
|
PRBool PrependElementUnlessExists(const Item& item) {
|
|
return Contains(item) || mArray.InsertElementAt(0, item) != nsnull;
|
|
}
|
|
|
|
// Append an element to the array.
|
|
// @param item The item to append.
|
|
// @return A pointer to the newly appended element, or null on OOM.
|
|
template<class Item>
|
|
elem_type* AppendElement(const Item& item) {
|
|
return mArray.AppendElement(item);
|
|
}
|
|
|
|
// Same as above, but without copy-constructing. This is useful to avoid
|
|
// temporaries.
|
|
elem_type* AppendElement() {
|
|
return mArray.AppendElement();
|
|
}
|
|
|
|
// Append an element to the array unless it already exists in the array.
|
|
// 'operator==' must be defined for elem_type.
|
|
// @param item The item to append.
|
|
// @return PR_TRUE if the element was found, or inserted successfully.
|
|
template<class Item>
|
|
PRBool AppendElementUnlessExists(const Item& item) {
|
|
return Contains(item) || AppendElement(item) != nsnull;
|
|
}
|
|
|
|
// Remove an element from the array.
|
|
// @param index The index of the item to remove.
|
|
void RemoveElementAt(index_type index) {
|
|
NS_ASSERTION(index < mArray.Length(), "invalid index");
|
|
mArray.RemoveElementAt(index);
|
|
AdjustIterators(index, -1);
|
|
}
|
|
|
|
// This helper function combines IndexOf with RemoveElementAt to "search
|
|
// and destroy" the first element that is equal to the given element.
|
|
// 'operator==' must be defined for elem_type.
|
|
// @param item The item to search for.
|
|
// @return PR_TRUE if the element was found and removed.
|
|
template<class Item>
|
|
PRBool RemoveElement(const Item& item) {
|
|
index_type index = mArray.IndexOf(item, 0);
|
|
if (index == array_type::NoIndex)
|
|
return PR_FALSE;
|
|
|
|
mArray.RemoveElementAt(index);
|
|
AdjustIterators(index, -1);
|
|
return PR_TRUE;
|
|
}
|
|
|
|
// Removes all observers and collapses all iterators to the beginning of
|
|
// the array. The result is that forward iterators will see all elements
|
|
// in the array.
|
|
void Clear() {
|
|
mArray.Clear();
|
|
ClearIterators();
|
|
}
|
|
|
|
//
|
|
// Iterators
|
|
//
|
|
|
|
// Base class for iterators. Do not use this directly.
|
|
class Iterator : public Iterator_base {
|
|
protected:
|
|
friend class nsAutoTObserverArray;
|
|
typedef nsAutoTObserverArray<T, N> array_type;
|
|
|
|
Iterator(index_type aPosition, const array_type& aArray)
|
|
: Iterator_base(aPosition, aArray.mIterators),
|
|
mArray(const_cast<array_type&>(aArray)) {
|
|
aArray.mIterators = this;
|
|
}
|
|
|
|
~Iterator() {
|
|
NS_ASSERTION(mArray.mIterators == this,
|
|
"Iterators must currently be destroyed in opposite order "
|
|
"from the construction order. It is suggested that you "
|
|
"simply put them on the stack");
|
|
mArray.mIterators = mNext;
|
|
}
|
|
|
|
// The array we're iterating
|
|
array_type& mArray;
|
|
};
|
|
|
|
// Iterates the array forward from beginning to end. mPosition points
|
|
// to the element that will be returned on next call to GetNext.
|
|
// Elements:
|
|
// - prepended to the array during iteration *will not* be traversed
|
|
// - appended during iteration *will* be traversed
|
|
// - removed during iteration *will not* be traversed.
|
|
// @see EndLimitedIterator
|
|
class ForwardIterator : protected Iterator {
|
|
public:
|
|
typedef nsAutoTObserverArray<T, N> array_type;
|
|
typedef Iterator base_type;
|
|
|
|
ForwardIterator(const array_type& aArray)
|
|
: Iterator(0, aArray) {
|
|
}
|
|
|
|
ForwardIterator(const array_type& aArray, index_type aPos)
|
|
: Iterator(aPos, aArray) {
|
|
}
|
|
|
|
PRBool operator <(const ForwardIterator& aOther) const {
|
|
NS_ASSERTION(&this->mArray == &aOther.mArray,
|
|
"not iterating the same array");
|
|
return base_type::mPosition < aOther.mPosition;
|
|
}
|
|
|
|
// Returns PR_TRUE if there are more elements to iterate.
|
|
// This must precede a call to GetNext(). If PR_FALSE is
|
|
// returned, GetNext() must not be called.
|
|
PRBool HasMore() const {
|
|
return base_type::mPosition < base_type::mArray.Length();
|
|
}
|
|
|
|
// Returns the next element and steps one step. This must
|
|
// be preceded by a call to HasMore().
|
|
// @return The next observer.
|
|
elem_type& GetNext() {
|
|
NS_ASSERTION(HasMore(), "iterating beyond end of array");
|
|
return base_type::mArray.ElementAt(base_type::mPosition++);
|
|
}
|
|
};
|
|
|
|
// EndLimitedIterator works like ForwardIterator, but will not iterate new
|
|
// observers appended to the array after the iterator was created.
|
|
class EndLimitedIterator : protected ForwardIterator {
|
|
public:
|
|
typedef nsAutoTObserverArray<T, N> array_type;
|
|
typedef Iterator base_type;
|
|
|
|
EndLimitedIterator(const array_type& aArray)
|
|
: ForwardIterator(aArray),
|
|
mEnd(aArray, aArray.Length()) {
|
|
}
|
|
|
|
// Returns PR_TRUE if there are more elements to iterate.
|
|
// This must precede a call to GetNext(). If PR_FALSE is
|
|
// returned, GetNext() must not be called.
|
|
PRBool HasMore() const {
|
|
return *this < mEnd;
|
|
}
|
|
|
|
// Returns the next element and steps one step. This must
|
|
// be preceded by a call to HasMore().
|
|
// @return The next observer.
|
|
elem_type& GetNext() {
|
|
NS_ASSERTION(HasMore(), "iterating beyond end of array");
|
|
return base_type::mArray.ElementAt(base_type::mPosition++);
|
|
}
|
|
|
|
private:
|
|
ForwardIterator mEnd;
|
|
};
|
|
|
|
protected:
|
|
nsAutoTArray<T, N> mArray;
|
|
};
|
|
|
|
template<class T>
|
|
class nsTObserverArray : public nsAutoTObserverArray<T, 0> {
|
|
public:
|
|
typedef nsAutoTObserverArray<T, 0> base_type;
|
|
typedef nsTObserverArray_base::size_type size_type;
|
|
|
|
//
|
|
// Initialization methods
|
|
//
|
|
|
|
nsTObserverArray() {}
|
|
|
|
// Initialize this array and pre-allocate some number of elements.
|
|
explicit nsTObserverArray(size_type capacity) {
|
|
base_type::mArray.SetCapacity(capacity);
|
|
}
|
|
};
|
|
|
|
// XXXbz I wish I didn't have to pass in the observer type, but I
|
|
// don't see a way to get it out of array_.
|
|
// Note that this macro only works if the array holds pointers to XPCOM objects.
|
|
#define NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(array_, obstype_, func_, params_) \
|
|
PR_BEGIN_MACRO \
|
|
nsTObserverArray<obstype_ *>::ForwardIterator iter_(array_); \
|
|
nsCOMPtr<obstype_> obs_; \
|
|
while (iter_.HasMore()) { \
|
|
obs_ = iter_.GetNext(); \
|
|
obs_ -> func_ params_ ; \
|
|
} \
|
|
PR_END_MACRO
|
|
|
|
// Note that this macro only works if the array holds pointers to XPCOM objects.
|
|
#define NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS(array_, obstype_, func_, params_) \
|
|
PR_BEGIN_MACRO \
|
|
nsTObserverArray<obstype_ *>::ForwardIterator iter_(array_); \
|
|
obstype_* obs_; \
|
|
while (iter_.HasMore()) { \
|
|
obs_ = iter_.GetNext(); \
|
|
obs_ -> func_ params_ ; \
|
|
} \
|
|
PR_END_MACRO
|
|
#endif // nsTObserverArray_h___
|