Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / content / xul / templates / src / nsTreeRows.cpp
blob0e67c12e3e30b3acc3d733a06a563ddecf922399
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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.
15 * The Original Code is Mozilla Communicator client code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * Chris Waterson <waterson@netscape.com>
25 * Alternatively, the contents of this file may be used under the terms of
26 * either of the GNU General Public License Version 2 or later (the "GPL"),
27 * or 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.
37 * ***** END LICENSE BLOCK ***** */
39 #include "nsString.h"
40 #include "nsTreeRows.h"
42 nsTreeRows::Subtree*
43 nsTreeRows::EnsureSubtreeFor(Subtree* aParent,
44 PRInt32 aChildIndex)
46 Subtree* subtree = GetSubtreeFor(aParent, aChildIndex);
48 if (! subtree) {
49 subtree = aParent->mRows[aChildIndex].mSubtree = new Subtree(aParent);
50 InvalidateCachedRow();
53 return subtree;
56 nsTreeRows::Subtree*
57 nsTreeRows::GetSubtreeFor(const Subtree* aParent,
58 PRInt32 aChildIndex,
59 PRInt32* aSubtreeSize)
61 NS_PRECONDITION(aParent, "no parent");
62 NS_PRECONDITION(aChildIndex >= 0, "bad child index");
64 Subtree* result = nsnull;
66 if (aChildIndex < aParent->mCount)
67 result = aParent->mRows[aChildIndex].mSubtree;
69 if (aSubtreeSize)
70 *aSubtreeSize = result ? result->mSubtreeSize : 0;
72 return result;
75 void
76 nsTreeRows::RemoveSubtreeFor(Subtree* aParent, PRInt32 aChildIndex)
78 NS_PRECONDITION(aParent, "no parent");
79 NS_PRECONDITION(aChildIndex >= 0 && aChildIndex < aParent->mCount, "bad child index");
81 Row& row = aParent->mRows[aChildIndex];
83 if (row.mSubtree) {
84 PRInt32 subtreeSize = row.mSubtree->GetSubtreeSize();
86 delete row.mSubtree;
87 row.mSubtree = nsnull;
89 for (Subtree* subtree = aParent; subtree != nsnull; subtree = subtree->mParent)
90 subtree->mSubtreeSize -= subtreeSize;
93 InvalidateCachedRow();
96 nsTreeRows::iterator
97 nsTreeRows::First()
99 iterator result;
100 result.Append(&mRoot, 0);
101 result.SetRowIndex(0);
102 return result;
105 nsTreeRows::iterator
106 nsTreeRows::Last()
108 iterator result;
110 // Build up a path along the rightmost edge of the tree
111 Subtree* current = &mRoot;
112 PRInt32 count = current->Count();
113 do {
114 PRInt32 last = count - 1;
115 result.Append(current, last);
116 current = count ? GetSubtreeFor(current, last) : nsnull;
117 } while (current && ((count = current->Count()) != 0));
119 // Now, at the bottom rightmost leaf, advance us one off the end.
120 result.GetTop().mChildIndex++;
122 // Our row index will be the size of the root subree, plus one.
123 result.SetRowIndex(mRoot.GetSubtreeSize() + 1);
125 return result;
128 nsTreeRows::iterator
129 nsTreeRows::operator[](PRInt32 aRow)
131 // See if we're just lucky, and end up with something
132 // nearby. (This tends to happen a lot due to the way that we get
133 // asked for rows n' stuff.)
134 PRInt32 last = mLastRow.GetRowIndex();
135 if (last != -1) {
136 if (aRow == last)
137 return mLastRow;
138 else if (last + 1 == aRow)
139 return ++mLastRow;
140 else if (last - 1 == aRow)
141 return --mLastRow;
144 // Nope. Construct a path to the specified index. This is a little
145 // bit better than O(n), because we can skip over subtrees. (So it
146 // ends up being approximately linear in the subtree size, instead
147 // of the entire view size. But, most of the time, big views are
148 // flat. Oh well.)
149 iterator result;
150 Subtree* current = &mRoot;
152 PRInt32 index = 0;
153 result.SetRowIndex(aRow);
155 do {
156 PRInt32 subtreeSize;
157 Subtree* subtree = GetSubtreeFor(current, index, &subtreeSize);
159 if (subtreeSize >= aRow) {
160 result.Append(current, index);
161 current = subtree;
162 index = 0;
163 --aRow;
165 else {
166 ++index;
167 aRow -= subtreeSize + 1;
169 } while (aRow >= 0);
171 mLastRow = result;
172 return result;
175 nsTreeRows::iterator
176 nsTreeRows::FindByResource(nsIRDFResource* aResource)
178 // XXX Mmm, scan through the rows one-by-one...
179 iterator last = Last();
180 iterator iter;
182 nsresult rv;
183 nsAutoString resourceid;
184 PRBool stringmode = PR_FALSE;
186 for (iter = First(); iter != last; ++iter) {
187 if (!stringmode) {
188 nsCOMPtr<nsIRDFResource> findres;
189 rv = iter->mMatch->mResult->GetResource(getter_AddRefs(findres));
190 if (NS_FAILED(rv)) return last;
192 if (findres == aResource)
193 break;
195 if (! findres) {
196 const char *uri;
197 aResource->GetValueConst(&uri);
198 CopyUTF8toUTF16(uri, resourceid);
200 // set stringmode and fall through
201 stringmode = PR_TRUE;
205 // additional check because previous block could change stringmode
206 if (stringmode) {
207 nsAutoString findid;
208 rv = iter->mMatch->mResult->GetId(findid);
209 if (NS_FAILED(rv)) return last;
211 if (resourceid.Equals(findid))
212 break;
216 return iter;
219 nsTreeRows::iterator
220 nsTreeRows::Find(nsIXULTemplateResult *aResult)
222 // XXX Mmm, scan through the rows one-by-one...
223 iterator last = Last();
224 iterator iter;
226 for (iter = First(); iter != last; ++iter) {
227 if (aResult == iter->mMatch->mResult)
228 break;
231 return iter;
234 void
235 nsTreeRows::Clear()
237 mRoot.Clear();
238 InvalidateCachedRow();
241 //----------------------------------------------------------------------
243 // nsTreeRows::Subtree
246 nsTreeRows::Subtree::~Subtree()
248 Clear();
251 void
252 nsTreeRows::Subtree::Clear()
254 for (PRInt32 i = mCount - 1; i >= 0; --i)
255 delete mRows[i].mSubtree;
257 delete[] mRows;
259 mRows = nsnull;
260 mCount = mCapacity = mSubtreeSize = 0;
263 nsTreeRows::iterator
264 nsTreeRows::Subtree::InsertRowAt(nsTemplateMatch* aMatch, PRInt32 aIndex)
266 if (mCount >= mCapacity || aIndex >= mCapacity) {
267 PRInt32 newCapacity = NS_MAX(mCapacity * 2, aIndex + 1);
268 Row* newRows = new Row[newCapacity];
269 if (! newRows)
270 return iterator();
272 for (PRInt32 i = mCount - 1; i >= 0; --i)
273 newRows[i] = mRows[i];
275 delete[] mRows;
277 mRows = newRows;
278 mCapacity = newCapacity;
281 for (PRInt32 i = mCount - 1; i >= aIndex; --i)
282 mRows[i + 1] = mRows[i];
284 mRows[aIndex].mMatch = aMatch;
285 mRows[aIndex].mContainerType = eContainerType_Unknown;
286 mRows[aIndex].mContainerState = eContainerState_Unknown;
287 mRows[aIndex].mContainerFill = eContainerFill_Unknown;
288 mRows[aIndex].mSubtree = nsnull;
289 ++mCount;
291 // Now build an iterator that points to the newly inserted element.
292 PRInt32 rowIndex = 0;
293 iterator result;
294 result.Push(this, aIndex);
296 for ( ; --aIndex >= 0; ++rowIndex) {
297 // Account for open subtrees in the absolute row index.
298 const Subtree *subtree = mRows[aIndex].mSubtree;
299 if (subtree)
300 rowIndex += subtree->mSubtreeSize;
303 Subtree *subtree = this;
304 do {
305 // Note that the subtree's size has expanded.
306 ++subtree->mSubtreeSize;
308 Subtree *parent = subtree->mParent;
309 if (! parent)
310 break;
312 // Account for open subtrees in the absolute row index.
313 PRInt32 count = parent->Count();
314 for (aIndex = 0; aIndex < count; ++aIndex, ++rowIndex) {
315 const Subtree *child = (*parent)[aIndex].mSubtree;
316 if (subtree == child)
317 break;
319 if (child)
320 rowIndex += child->mSubtreeSize;
323 NS_ASSERTION(aIndex < count, "couldn't find subtree in parent");
325 result.Push(parent, aIndex);
326 subtree = parent;
327 ++rowIndex; // One for the parent row.
328 } while (1);
330 result.SetRowIndex(rowIndex);
331 return result;
334 void
335 nsTreeRows::Subtree::RemoveRowAt(PRInt32 aIndex)
337 NS_PRECONDITION(aIndex >= 0 && aIndex < Count(), "bad index");
338 if (aIndex < 0 || aIndex >= Count())
339 return;
341 // How big is the subtree we're going to be removing?
342 PRInt32 subtreeSize = mRows[aIndex].mSubtree
343 ? mRows[aIndex].mSubtree->GetSubtreeSize()
344 : 0;
346 ++subtreeSize;
348 delete mRows[aIndex].mSubtree;
350 for (PRInt32 i = aIndex + 1; i < mCount; ++i)
351 mRows[i - 1] = mRows[i];
353 --mCount;
355 for (Subtree* subtree = this; subtree != nsnull; subtree = subtree->mParent)
356 subtree->mSubtreeSize -= subtreeSize;
359 //----------------------------------------------------------------------
361 // nsTreeRows::iterator
364 nsTreeRows::iterator::iterator(const iterator& aIterator)
365 : mRowIndex(aIterator.mRowIndex),
366 mLink(aIterator.mLink)
370 nsTreeRows::iterator&
371 nsTreeRows::iterator::operator=(const iterator& aIterator)
373 mRowIndex = aIterator.mRowIndex;
374 mLink = aIterator.mLink;
375 return *this;
378 void
379 nsTreeRows::iterator::Append(Subtree* aParent, PRInt32 aChildIndex)
381 Link *link = mLink.AppendElement();
382 if (link) {
383 link->mParent = aParent;
384 link->mChildIndex = aChildIndex;
386 else
387 NS_ERROR("out of memory");
390 void
391 nsTreeRows::iterator::Push(Subtree *aParent, PRInt32 aChildIndex)
393 Link *link = mLink.InsertElementAt(0);
394 if (link) {
395 link->mParent = aParent;
396 link->mChildIndex = aChildIndex;
398 else
399 NS_ERROR("out of memory");
402 PRBool
403 nsTreeRows::iterator::operator==(const iterator& aIterator) const
405 if (GetDepth() != aIterator.GetDepth())
406 return PR_FALSE;
408 if (GetDepth() == 0)
409 return PR_TRUE;
411 return GetTop() == aIterator.GetTop();
414 void
415 nsTreeRows::iterator::Next()
417 NS_PRECONDITION(GetDepth() > 0, "cannot increment an uninitialized iterator");
419 // Increment the absolute row index
420 ++mRowIndex;
422 Link& top = GetTop();
424 // Is there a child subtree? If so, descend into the child
425 // subtree.
426 Subtree* subtree = top.GetRow().mSubtree;
428 if (subtree && subtree->Count()) {
429 Append(subtree, 0);
430 return;
433 // Have we exhausted the current subtree?
434 if (top.mChildIndex >= top.mParent->Count() - 1) {
435 // Yep. See if we've just iterated path the last element in
436 // the tree, period. Walk back up the stack, looking for any
437 // unfinished subtrees.
438 PRInt32 unfinished;
439 for (unfinished = GetDepth() - 2; unfinished >= 0; --unfinished) {
440 const Link& link = mLink[unfinished];
441 if (link.mChildIndex < link.mParent->Count() - 1)
442 break;
445 // If there are no unfinished subtrees in the stack, then this
446 // iterator is exhausted. Leave it in the same state that
447 // Last() does.
448 if (unfinished < 0) {
449 top.mChildIndex++;
450 return;
453 // Otherwise, we ran off the end of one of the inner
454 // subtrees. Pop up to the next unfinished level in the stack.
455 mLink.SetLength(unfinished + 1);
458 // Advance to the next child in this subtree
459 ++(GetTop().mChildIndex);
462 void
463 nsTreeRows::iterator::Prev()
465 NS_PRECONDITION(GetDepth() > 0, "cannot increment an uninitialized iterator");
467 // Decrement the absolute row index
468 --mRowIndex;
470 // Move to the previous child in this subtree
471 --(GetTop().mChildIndex);
473 // Have we exhausted the current subtree?
474 if (GetTop().mChildIndex < 0) {
475 // Yep. See if we've just iterated back to the first element
476 // in the tree, period. Walk back up the stack, looking for
477 // any unfinished subtrees.
478 PRInt32 unfinished;
479 for (unfinished = GetDepth() - 2; unfinished >= 0; --unfinished) {
480 const Link& link = mLink[unfinished];
481 if (link.mChildIndex >= 0)
482 break;
485 // If there are no unfinished subtrees in the stack, then this
486 // iterator is exhausted. Leave it in the same state that
487 // First() does.
488 if (unfinished < 0)
489 return;
491 // Otherwise, we ran off the end of one of the inner
492 // subtrees. Pop up to the next unfinished level in the stack.
493 mLink.SetLength(unfinished + 1);
494 return;
497 // Is there a child subtree immediately prior to our current
498 // position? If so, descend into it, grovelling down to the
499 // deepest, rightmost left edge.
500 Subtree* parent = GetTop().GetParent();
501 PRInt32 index = GetTop().GetChildIndex();
503 Subtree* subtree = (*parent)[index].mSubtree;
505 if (subtree && subtree->Count()) {
506 do {
507 index = subtree->Count() - 1;
508 Append(subtree, index);
510 parent = subtree;
511 subtree = (*parent)[index].mSubtree;
512 } while (subtree && subtree->Count());