/gecko_api/include/nsTObserverArray.h
C++ Header | 382 lines | 177 code | 57 blank | 148 comment | 11 complexity | 32150759fc5a9a4579174fa19cb0d832 MD5 | raw file
1/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2/* ***** BEGIN LICENSE BLOCK ***** 3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 4 * 5 * The contents of this file are subject to the Mozilla Public License Version 6 * 1.1 (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * http://www.mozilla.org/MPL/ 9 * 10 * Software distributed under the License is distributed on an "AS IS" basis, 11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 12 * for the specific language governing rights and limitations under the 13 * License. 14 * 15 * The Original Code is Mozilla.org code. 16 * 17 * The Initial Developer of the Original Code is Mozilla Corporation. 18 * Portions created by the Initial Developer are Copyright (C) 2006 19 * the Initial Developer. All Rights Reserved. 20 * 21 * Contributor(s): 22 * Jonas Sicking <jonas@sicking.cc> (Original Author) 23 * Daniel Witte <dwitte@stanford.edu> 24 * 25 * Alternatively, the contents of this file may be used under the terms of 26 * either the GNU General Public License Version 2 or later (the "GPL"), or 27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), 28 * in which case the provisions of the GPL or the LGPL are applicable instead 29 * of those above. If you wish to allow use of your version of this file only 30 * under the terms of either the GPL or the LGPL, and not to allow others to 31 * use your version of this file under the terms of the MPL, indicate your 32 * decision by deleting the provisions above and replace them with the notice 33 * and other provisions required by the GPL or the LGPL. If you do not delete 34 * the provisions above, a recipient may use your version of this file under 35 * the terms of any one of the MPL, the GPL or the LGPL. 36 * 37 * ***** END LICENSE BLOCK ***** */ 38 39#ifndef nsTObserverArray_h___ 40#define nsTObserverArray_h___ 41 42#include "nsTArray.h" 43 44class NS_COM_GLUE nsTObserverArray_base { 45 public: 46 typedef PRUint32 index_type; 47 typedef PRUint32 size_type; 48 typedef PRInt32 diff_type; 49 50 protected: 51 class Iterator_base { 52 protected: 53 friend class nsTObserverArray_base; 54 55 Iterator_base(index_type aPosition, Iterator_base* aNext) 56 : mPosition(aPosition), 57 mNext(aNext) { 58 } 59 60 // The current position of the iterator. Its exact meaning differs 61 // depending on iterator. See nsTObserverArray<T>::ForwardIterator. 62 index_type mPosition; 63 64 // The next iterator currently iterating the same array 65 Iterator_base* mNext; 66 }; 67 68 nsTObserverArray_base() 69 : mIterators(nsnull) { 70 } 71 72 ~nsTObserverArray_base() { 73 NS_ASSERTION(mIterators == nsnull, "iterators outlasting array"); 74 } 75 76 /** 77 * Adjusts iterators after an element has been inserted or removed 78 * from the array. 79 * @param modPos Position where elements were added or removed. 80 * @param adjustment -1 if an element was removed, 1 if an element was 81 * added. 82 */ 83 void AdjustIterators(index_type aModPos, diff_type aAdjustment); 84 85 /** 86 * Clears iterators when the array is destroyed. 87 */ 88 void ClearIterators(); 89 90 mutable Iterator_base* mIterators; 91}; 92 93/** 94 * An array of observers. Like a normal array, but supports iterators that are 95 * stable even if the array is modified during iteration. 96 * The template parameter T is the observer type the array will hold; 97 * N is the number of built-in storage slots that come with the array. 98 * NOTE: You probably want to use nsTObserverArray, unless you specifically 99 * want built-in storage. See below. 100 * @see nsTObserverArray, nsTArray 101 */ 102 103template<class T, PRUint32 N> 104class nsAutoTObserverArray : protected nsTObserverArray_base { 105 public: 106 typedef T elem_type; 107 typedef nsTArray<T> array_type; 108 109 nsAutoTObserverArray() { 110 } 111 112 // 113 // Accessor methods 114 // 115 116 // @return The number of elements in the array. 117 size_type Length() const { 118 return mArray.Length(); 119 } 120 121 // @return True if the array is empty or false otherwise. 122 PRBool IsEmpty() const { 123 return mArray.IsEmpty(); 124 } 125 126 // This method provides direct access to the i'th element of the array. 127 // The given index must be within the array bounds. 128 // @param i The index of an element in the array. 129 // @return A reference to the i'th element of the array. 130 elem_type& ElementAt(index_type i) { 131 return mArray.ElementAt(i); 132 } 133 134 // Same as above, but readonly. 135 const elem_type& ElementAt(index_type i) const { 136 return mArray.ElementAt(i); 137 } 138 139 // This method provides direct access to the i'th element of the array in 140 // a bounds safe manner. If the requested index is out of bounds the 141 // provided default value is returned. 142 // @param i The index of an element in the array. 143 // @param def The value to return if the index is out of bounds. 144 elem_type& SafeElementAt(index_type i, elem_type& def) { 145 return mArray.SafeElementAt(i, def); 146 } 147 148 // Same as above, but readonly. 149 const elem_type& SafeElementAt(index_type i, const elem_type& def) const { 150 return mArray.SafeElementAt(i, def); 151 } 152 153 // 154 // Search methods 155 // 156 157 // This method searches, starting from the beginning of the array, 158 // for the first element in this array that is equal to the given element. 159 // 'operator==' must be defined for elem_type. 160 // @param item The item to search for. 161 // @return PR_TRUE if the element was found. 162 template<class Item> 163 PRBool Contains(const Item& item) const { 164 return IndexOf(item) != array_type::NoIndex; 165 } 166 167 // This method searches for the offset of the first element in this 168 // array that is equal to the given element. 169 // 'operator==' must be defined for elem_type. 170 // @param item The item to search for. 171 // @param start The index to start from. 172 // @return The index of the found element or NoIndex if not found. 173 template<class Item> 174 index_type IndexOf(const Item& item, index_type start = 0) const { 175 return mArray.IndexOf(item, start); 176 } 177 178 // 179 // Mutation methods 180 // 181 182 // Prepend an element to the array unless it already exists in the array. 183 // 'operator==' must be defined for elem_type. 184 // @param item The item to prepend. 185 // @return PR_TRUE if the element was found, or inserted successfully. 186 template<class Item> 187 PRBool PrependElementUnlessExists(const Item& item) { 188 return Contains(item) || mArray.InsertElementAt(0, item) != nsnull; 189 } 190 191 // Append an element to the array. 192 // @param item The item to append. 193 // @return A pointer to the newly appended element, or null on OOM. 194 template<class Item> 195 elem_type* AppendElement(const Item& item) { 196 return mArray.AppendElement(item); 197 } 198 199 // Same as above, but without copy-constructing. This is useful to avoid 200 // temporaries. 201 elem_type* AppendElement() { 202 return mArray.AppendElement(); 203 } 204 205 // Append an element to the array unless it already exists in the array. 206 // 'operator==' must be defined for elem_type. 207 // @param item The item to append. 208 // @return PR_TRUE if the element was found, or inserted successfully. 209 template<class Item> 210 PRBool AppendElementUnlessExists(const Item& item) { 211 return Contains(item) || AppendElement(item) != nsnull; 212 } 213 214 // Remove an element from the array. 215 // @param index The index of the item to remove. 216 void RemoveElementAt(index_type index) { 217 NS_ASSERTION(index < mArray.Length(), "invalid index"); 218 mArray.RemoveElementAt(index); 219 AdjustIterators(index, -1); 220 } 221 222 // This helper function combines IndexOf with RemoveElementAt to "search 223 // and destroy" the first element that is equal to the given element. 224 // 'operator==' must be defined for elem_type. 225 // @param item The item to search for. 226 // @return PR_TRUE if the element was found and removed. 227 template<class Item> 228 PRBool RemoveElement(const Item& item) { 229 index_type index = mArray.IndexOf(item, 0); 230 if (index == array_type::NoIndex) 231 return PR_FALSE; 232 233 mArray.RemoveElementAt(index); 234 AdjustIterators(index, -1); 235 return PR_TRUE; 236 } 237 238 // Removes all observers and collapses all iterators to the beginning of 239 // the array. The result is that forward iterators will see all elements 240 // in the array. 241 void Clear() { 242 mArray.Clear(); 243 ClearIterators(); 244 } 245 246 // 247 // Iterators 248 // 249 250 // Base class for iterators. Do not use this directly. 251 class Iterator : public Iterator_base { 252 protected: 253 friend class nsAutoTObserverArray; 254 typedef nsAutoTObserverArray<T, N> array_type; 255 256 Iterator(index_type aPosition, const array_type& aArray) 257 : Iterator_base(aPosition, aArray.mIterators), 258 mArray(const_cast<array_type&>(aArray)) { 259 aArray.mIterators = this; 260 } 261 262 ~Iterator() { 263 NS_ASSERTION(mArray.mIterators == this, 264 "Iterators must currently be destroyed in opposite order " 265 "from the construction order. It is suggested that you " 266 "simply put them on the stack"); 267 mArray.mIterators = mNext; 268 } 269 270 // The array we're iterating 271 array_type& mArray; 272 }; 273 274 // Iterates the array forward from beginning to end. mPosition points 275 // to the element that will be returned on next call to GetNext. 276 // Elements: 277 // - prepended to the array during iteration *will not* be traversed 278 // - appended during iteration *will* be traversed 279 // - removed during iteration *will not* be traversed. 280 // @see EndLimitedIterator 281 class ForwardIterator : protected Iterator { 282 public: 283 typedef nsAutoTObserverArray<T, N> array_type; 284 typedef Iterator base_type; 285 286 ForwardIterator(const array_type& aArray) 287 : Iterator(0, aArray) { 288 } 289 290 ForwardIterator(const array_type& aArray, index_type aPos) 291 : Iterator(aPos, aArray) { 292 } 293 294 PRBool operator <(const ForwardIterator& aOther) const { 295 NS_ASSERTION(&this->mArray == &aOther.mArray, 296 "not iterating the same array"); 297 return base_type::mPosition < aOther.mPosition; 298 } 299 300 // Returns PR_TRUE if there are more elements to iterate. 301 // This must precede a call to GetNext(). If PR_FALSE is 302 // returned, GetNext() must not be called. 303 PRBool HasMore() const { 304 return base_type::mPosition < base_type::mArray.Length(); 305 } 306 307 // Returns the next element and steps one step. This must 308 // be preceded by a call to HasMore(). 309 // @return The next observer. 310 elem_type& GetNext() { 311 NS_ASSERTION(HasMore(), "iterating beyond end of array"); 312 return base_type::mArray.ElementAt(base_type::mPosition++); 313 } 314 }; 315 316 // EndLimitedIterator works like ForwardIterator, but will not iterate new 317 // observers appended to the array after the iterator was created. 318 class EndLimitedIterator : protected ForwardIterator { 319 public: 320 typedef nsAutoTObserverArray<T, N> array_type; 321 typedef Iterator base_type; 322 323 EndLimitedIterator(const array_type& aArray) 324 : ForwardIterator(aArray), 325 mEnd(aArray, aArray.Length()) { 326 } 327 328 // Returns PR_TRUE if there are more elements to iterate. 329 // This must precede a call to GetNext(). If PR_FALSE is 330 // returned, GetNext() must not be called. 331 PRBool HasMore() const { 332 return *this < mEnd; 333 } 334 335 // Returns the next element and steps one step. This must 336 // be preceded by a call to HasMore(). 337 // @return The next observer. 338 elem_type& GetNext() { 339 NS_ASSERTION(HasMore(), "iterating beyond end of array"); 340 return base_type::mArray.ElementAt(base_type::mPosition++); 341 } 342 343 private: 344 ForwardIterator mEnd; 345 }; 346 347 protected: 348 nsAutoTArray<T, N> mArray; 349}; 350 351template<class T> 352class nsTObserverArray : public nsAutoTObserverArray<T, 0> { 353 public: 354 typedef nsAutoTObserverArray<T, 0> base_type; 355 typedef nsTObserverArray_base::size_type size_type; 356 357 // 358 // Initialization methods 359 // 360 361 nsTObserverArray() {} 362 363 // Initialize this array and pre-allocate some number of elements. 364 explicit nsTObserverArray(size_type capacity) { 365 base_type::mArray.SetCapacity(capacity); 366 } 367}; 368 369// XXXbz I wish I didn't have to pass in the observer type, but I 370// don't see a way to get it out of array_. 371// Note that this macro only works if the array holds pointers to XPCOM objects. 372#define NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS(array_, obstype_, func_, params_) \ 373 PR_BEGIN_MACRO \ 374 nsTObserverArray<obstype_ *>::ForwardIterator iter_(array_); \ 375 nsCOMPtr<obstype_> obs_; \ 376 while (iter_.HasMore()) { \ 377 obs_ = iter_.GetNext(); \ 378 obs_ -> func_ params_ ; \ 379 } \ 380 PR_END_MACRO 381 382#endif // nsTObserverArray_h___