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 Foundation.
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 :
44 : /**
45 : * An array of observers. Like a normal array, but supports iterators that are
46 : * stable even if the array is modified during iteration.
47 : * The template parameter T is the observer type the array will hold;
48 : * N is the number of built-in storage slots that come with the array.
49 : * NOTE: You probably want to use nsTObserverArray, unless you specifically
50 : * want built-in storage. See below.
51 : * @see nsTObserverArray, nsTArray
52 : */
53 :
54 : class NS_COM_GLUE nsTObserverArray_base {
55 : public:
56 : typedef PRUint32 index_type;
57 : typedef PRUint32 size_type;
58 : typedef PRInt32 diff_type;
59 :
60 : protected:
61 : class Iterator_base {
62 : protected:
63 : friend class nsTObserverArray_base;
64 :
65 385871 : Iterator_base(index_type aPosition, Iterator_base* aNext)
66 : : mPosition(aPosition),
67 385871 : mNext(aNext) {
68 385871 : }
69 :
70 : // The current position of the iterator. Its exact meaning differs
71 : // depending on iterator. See nsTObserverArray<T>::ForwardIterator.
72 : index_type mPosition;
73 :
74 : // The next iterator currently iterating the same array
75 : Iterator_base* mNext;
76 : };
77 :
78 17871 : nsTObserverArray_base()
79 17871 : : mIterators(nsnull) {
80 17871 : }
81 :
82 17852 : ~nsTObserverArray_base() {
83 17852 : NS_ASSERTION(mIterators == nsnull, "iterators outlasting array");
84 17852 : }
85 :
86 : /**
87 : * Adjusts iterators after an element has been inserted or removed
88 : * from the array.
89 : * @param modPos Position where elements were added or removed.
90 : * @param adjustment -1 if an element was removed, 1 if an element was
91 : * added.
92 : */
93 : void AdjustIterators(index_type aModPos, diff_type aAdjustment);
94 :
95 : /**
96 : * Clears iterators when the array is destroyed.
97 : */
98 : void ClearIterators();
99 :
100 : mutable Iterator_base* mIterators;
101 : };
102 :
103 : template<class T, PRUint32 N>
104 17852 : class nsAutoTObserverArray : protected nsTObserverArray_base {
105 : public:
106 : typedef T elem_type;
107 : typedef nsTArray<T> array_type;
108 :
109 17871 : nsAutoTObserverArray() {
110 17871 : }
111 :
112 : //
113 : // Accessor methods
114 : //
115 :
116 : // @return The number of elements in the array.
117 1120645 : size_type Length() const {
118 1120645 : return mArray.Length();
119 : }
120 :
121 : // @return True if the array is empty or false otherwise.
122 639331 : bool IsEmpty() const {
123 639331 : 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 387681 : elem_type& ElementAt(index_type i) {
131 387681 : return mArray.ElementAt(i);
132 : }
133 :
134 : // Same as above, but readonly.
135 0 : const elem_type& ElementAt(index_type i) const {
136 0 : 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 0 : const elem_type& SafeElementAt(index_type i, const elem_type& def) const {
150 0 : 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 true if the element was found.
162 : template<class Item>
163 3186 : bool Contains(const Item& item) const {
164 3186 : 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 9571 : index_type IndexOf(const Item& item, index_type start = 0) const {
175 9571 : 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 true if the element was found, or inserted successfully.
186 : template<class Item>
187 1275 : bool PrependElementUnlessExists(const Item& item) {
188 1275 : 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 8303 : elem_type* AppendElement(const Item& item) {
196 8303 : return mArray.AppendElement(item);
197 : }
198 :
199 : // Same as above, but without copy-constructing. This is useful to avoid
200 : // temporaries.
201 4584 : elem_type* AppendElement() {
202 4584 : 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 true if the element was found, or inserted successfully.
209 : template<class Item>
210 29 : bool AppendElementUnlessExists(const Item& item) {
211 29 : 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 34 : void RemoveElementAt(index_type index) {
217 34 : NS_ASSERTION(index < mArray.Length(), "invalid index");
218 34 : mArray.RemoveElementAt(index);
219 34 : AdjustIterators(index, -1);
220 34 : }
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 true if the element was found and removed.
227 : template<class Item>
228 9064 : bool RemoveElement(const Item& item) {
229 9064 : index_type index = mArray.IndexOf(item, 0);
230 9064 : if (index == array_type::NoIndex)
231 2200 : return false;
232 :
233 6864 : mArray.RemoveElementAt(index);
234 6864 : AdjustIterators(index, -1);
235 6864 : return 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 15132 : void Clear() {
242 15132 : mArray.Clear();
243 15132 : ClearIterators();
244 15132 : }
245 :
246 : // Returns the number of bytes on the heap taken up by this object, not
247 : // including sizeof(*this).
248 0 : size_t SizeOfExcludingThis(nsMallocSizeOfFun mallocSizeOf) const {
249 0 : return mArray.SizeOfExcludingThis(mallocSizeOf);
250 : }
251 :
252 : //
253 : // Iterators
254 : //
255 :
256 : // Base class for iterators. Do not use this directly.
257 : class Iterator : public Iterator_base {
258 : protected:
259 : friend class nsAutoTObserverArray;
260 : typedef nsAutoTObserverArray<T, N> array_type;
261 :
262 385871 : Iterator(index_type aPosition, const array_type& aArray)
263 : : Iterator_base(aPosition, aArray.mIterators),
264 385871 : mArray(const_cast<array_type&>(aArray)) {
265 385871 : aArray.mIterators = this;
266 385871 : }
267 :
268 385871 : ~Iterator() {
269 385871 : NS_ASSERTION(mArray.mIterators == this,
270 : "Iterators must currently be destroyed in opposite order "
271 : "from the construction order. It is suggested that you "
272 : "simply put them on the stack");
273 385871 : mArray.mIterators = mNext;
274 385871 : }
275 :
276 : // The array we're iterating
277 : array_type& mArray;
278 : };
279 :
280 : // Iterates the array forward from beginning to end. mPosition points
281 : // to the element that will be returned on next call to GetNext.
282 : // Elements:
283 : // - prepended to the array during iteration *will not* be traversed
284 : // - appended during iteration *will* be traversed
285 : // - removed during iteration *will not* be traversed.
286 : // @see EndLimitedIterator
287 385871 : class ForwardIterator : protected Iterator {
288 : public:
289 : typedef nsAutoTObserverArray<T, N> array_type;
290 : typedef Iterator base_type;
291 :
292 377637 : ForwardIterator(const array_type& aArray)
293 377637 : : Iterator(0, aArray) {
294 377637 : }
295 :
296 8234 : ForwardIterator(const array_type& aArray, index_type aPos)
297 8234 : : Iterator(aPos, aArray) {
298 8234 : }
299 :
300 36392 : bool operator <(const ForwardIterator& aOther) const {
301 36392 : NS_ASSERTION(&this->mArray == &aOther.mArray,
302 : "not iterating the same array");
303 36392 : return base_type::mPosition < aOther.mPosition;
304 : }
305 :
306 : // Returns true if there are more elements to iterate.
307 : // This must precede a call to GetNext(). If false is
308 : // returned, GetNext() must not be called.
309 1105663 : bool HasMore() const {
310 1105663 : return base_type::mPosition < base_type::mArray.Length();
311 : }
312 :
313 : // Returns the next element and steps one step. This must
314 : // be preceded by a call to HasMore().
315 : // @return The next observer.
316 368125 : elem_type& GetNext() {
317 368125 : NS_ASSERTION(HasMore(), "iterating beyond end of array");
318 368125 : return base_type::mArray.ElementAt(base_type::mPosition++);
319 : }
320 : };
321 :
322 : // EndLimitedIterator works like ForwardIterator, but will not iterate new
323 : // observers appended to the array after the iterator was created.
324 8234 : class EndLimitedIterator : protected ForwardIterator {
325 : public:
326 : typedef nsAutoTObserverArray<T, N> array_type;
327 : typedef Iterator base_type;
328 :
329 8234 : EndLimitedIterator(const array_type& aArray)
330 : : ForwardIterator(aArray),
331 8234 : mEnd(aArray, aArray.Length()) {
332 8234 : }
333 :
334 : // Returns true if there are more elements to iterate.
335 : // This must precede a call to GetNext(). If false is
336 : // returned, GetNext() must not be called.
337 36392 : bool HasMore() const {
338 36392 : return *this < mEnd;
339 : }
340 :
341 : // Returns the next element and steps one step. This must
342 : // be preceded by a call to HasMore().
343 : // @return The next observer.
344 14078 : elem_type& GetNext() {
345 14078 : NS_ASSERTION(HasMore(), "iterating beyond end of array");
346 14078 : return base_type::mArray.ElementAt(base_type::mPosition++);
347 : }
348 :
349 : private:
350 : ForwardIterator mEnd;
351 : };
352 :
353 : protected:
354 : nsAutoTArray<T, N> mArray;
355 : };
356 :
357 : template<class T>
358 7170 : class nsTObserverArray : public nsAutoTObserverArray<T, 0> {
359 : public:
360 : typedef nsAutoTObserverArray<T, 0> base_type;
361 : typedef nsTObserverArray_base::size_type size_type;
362 :
363 : //
364 : // Initialization methods
365 : //
366 :
367 7176 : nsTObserverArray() {}
368 :
369 : // Initialize this array and pre-allocate some number of elements.
370 : explicit nsTObserverArray(size_type capacity) {
371 : base_type::mArray.SetCapacity(capacity);
372 : }
373 : };
374 :
375 : // XXXbz I wish I didn't have to pass in the observer type, but I
376 : // don't see a way to get it out of array_.
377 : // Note that this macro only works if the array holds pointers to XPCOM objects.
378 : #define NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(array_, obstype_, func_, params_) \
379 : PR_BEGIN_MACRO \
380 : nsTObserverArray<obstype_ *>::ForwardIterator iter_(array_); \
381 : nsCOMPtr<obstype_> obs_; \
382 : while (iter_.HasMore()) { \
383 : obs_ = iter_.GetNext(); \
384 : obs_ -> func_ params_ ; \
385 : } \
386 : PR_END_MACRO
387 :
388 : // Note that this macro only works if the array holds pointers to XPCOM objects.
389 : #define NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS(array_, obstype_, func_, params_) \
390 : PR_BEGIN_MACRO \
391 : nsTObserverArray<obstype_ *>::ForwardIterator iter_(array_); \
392 : obstype_* obs_; \
393 : while (iter_.HasMore()) { \
394 : obs_ = iter_.GetNext(); \
395 : obs_ -> func_ params_ ; \
396 : } \
397 : PR_END_MACRO
398 : #endif // nsTObserverArray_h___
|