xref: /aoo41x/main/sot/source/sdstor/stgavl.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_sot.hxx"
30 
31 
32 #include "stgavl.hxx"
33 
34 StgAvlNode::StgAvlNode()
35 {
36     pLeft = pRight = NULL;
37     nBalance = nId = 0;
38 }
39 
40 StgAvlNode::~StgAvlNode()
41 {
42     delete pLeft;
43     delete pRight;
44 }
45 
46 StgAvlNode* StgAvlNode::Find( StgAvlNode* pFind )
47 {
48     StgAvlNode* p = this;
49     while( p )
50     {
51         short nRes = p->Compare( pFind );
52         if( !nRes )
53             return p;
54         else p = ( nRes < 0 ) ? p->pLeft : p->pRight;
55     }
56     return NULL;
57 }
58 
59 // find point to add node to AVL tree and returns
60 // +/0/- for >/=/< previous
61 
62 short StgAvlNode::Locate
63     ( StgAvlNode* pFind,
64       StgAvlNode** pPivot, StgAvlNode **pParent, StgAvlNode** pPrev )
65 {
66     short nRes = 0;
67     StgAvlNode* pCur = this;
68     *pParent = *pPrev = NULL;
69     *pPivot = this;
70 
71     // search tree for insertion point
72 
73     while( pCur != NULL )
74     {
75         // check for pPivot
76         if( pCur->nBalance != 0 )
77             *pPivot = pCur, *pParent = *pPrev;
78         // save pPrev location and see what direction to go
79         *pPrev = pCur;
80         nRes = pCur->Compare( pFind );
81         if( nRes == 0 )
82             break;
83         else pCur = ( nRes < 0 ) ? pCur->pLeft : pCur->pRight;
84     }
85     return( nRes );
86 }
87 
88 // adjust balance factors in AVL tree from pivot down.
89 // Returns delta balance.
90 
91 short StgAvlNode::Adjust( StgAvlNode** pHeavy, StgAvlNode* pNew )
92 {
93     StgAvlNode* pCur = this;
94     short nDelta;
95     // no traversing
96     if( pCur == pNew )
97         return nBalance;
98     short nRes = Compare( pNew );
99     if( nRes > 0 )
100     {
101         *pHeavy = pCur = pRight;
102         nDelta = -1;
103     }
104     else
105     {
106         *pHeavy = pCur = pLeft;
107         nDelta = 1;
108     }
109 	nBalance = 0;
110 	while( pCur != pNew )
111     {
112         nRes = pCur->Compare( pNew );
113         if( nRes > 0 )
114         {
115             // height of right increases by 1
116             pCur->nBalance = -1;
117             pCur = pCur->pRight;
118         }
119         else
120         {
121             // height of left increases by 1
122             pCur->nBalance = 1;
123             pCur = pCur->pLeft;
124         }
125     }
126     nBalance = nBalance + nDelta;
127     return nDelta;
128 }
129 
130 // perform LL rotation and return new root
131 
132 StgAvlNode* StgAvlNode::RotLL()
133 {
134     StgAvlNode *pHeavy = pLeft;
135     pLeft = pHeavy->pRight;
136     pHeavy->pRight = this;
137     pHeavy->nBalance = nBalance = 0;
138     return pHeavy;
139 }
140 
141 // perform LR rotation and return new root
142 
143 StgAvlNode* StgAvlNode::RotLR()
144 {
145 
146     StgAvlNode* pHeavy = pLeft;
147     StgAvlNode* pNewRoot = pHeavy->pRight;
148 
149     pHeavy->pRight = pNewRoot->pLeft;
150     pLeft = pNewRoot->pRight;
151     pNewRoot->pLeft = pHeavy;
152     pNewRoot->pRight = this;
153 
154     switch( pNewRoot->nBalance )
155     {
156         case 1:     // LR( b )
157             nBalance = -1;
158             pHeavy->nBalance = 0;
159             break;
160         case -1:    // LR( c )
161             pHeavy->nBalance = 1;
162             nBalance = 0;
163             break;
164         case 0:     // LR( a )
165             nBalance = 0;
166             pHeavy->nBalance = 0;
167             break;
168     }
169     pNewRoot->nBalance = 0;
170     return pNewRoot;
171 }
172 
173 // perform RR rotation and return new root
174 
175 StgAvlNode* StgAvlNode::RotRR()
176 {
177     StgAvlNode* pHeavy = pRight;
178     pRight = pHeavy->pLeft;
179     pHeavy->pLeft = this;
180     nBalance = pHeavy->nBalance = 0;
181     return pHeavy;
182 }
183 
184 // perform the RL rotation and return the new root
185 
186 StgAvlNode* StgAvlNode::RotRL()
187 {
188     StgAvlNode* pHeavy = pRight;
189     StgAvlNode* pNewRoot = pHeavy->pLeft;
190     pHeavy->pLeft = pNewRoot->pRight;
191     pRight = pNewRoot->pLeft;
192     pNewRoot->pRight = pHeavy;
193     pNewRoot->pLeft = this;
194     switch( pNewRoot->nBalance )
195     {
196         case -1:    // RL( b )
197             nBalance = 1;
198             pHeavy->nBalance = 0;
199             break;
200         case 1:     // RL( c )
201             pHeavy->nBalance = -1;
202             nBalance = 0;
203             break;
204         case 0:     // RL( a )
205             nBalance = 0;
206             pHeavy->nBalance = 0;
207             break;
208     }
209     pNewRoot->nBalance = 0;
210     return pNewRoot;
211 }
212 
213 // Remove a tree element. Return the removed element or NULL.
214 
215 StgAvlNode* StgAvlNode::Rem( StgAvlNode** p, StgAvlNode* pDel, sal_Bool bPtrs )
216 {
217     if( *p )
218     {
219         StgAvlNode* pCur = *p;
220         short nRes = bPtrs ? short( pCur == pDel ) : short(pCur->Compare( pDel ));
221         if( !nRes )
222         {
223             // Element found: remove
224             if( !pCur->pRight )
225             {
226                 *p = pCur->pLeft; pCur->pLeft = NULL;
227             }
228             else if( !pCur->pLeft )
229             {
230                 *p = pCur->pRight; pCur->pRight = NULL;
231             }
232             else
233             {
234                 // The damn element has two leaves. Get the
235                 // rightmost element of the left subtree (which
236                 // is lexically before this element) and replace
237                 // this element with the element found.
238                 StgAvlNode* last = pCur;
239                 StgAvlNode* l;
240                 for( l = pCur->pLeft;
241                      l->pRight; last = l, l = l->pRight ) {}
242                 // remove the element from chain
243                 if( l == last->pRight )
244                     last->pRight = l->pLeft;
245                 else
246                     last->pLeft = l->pLeft;
247                 // perform the replacement
248                 l->pLeft = pCur->pLeft;
249                 l->pRight = pCur->pRight;
250                 *p = l;
251                 // delete the element
252                 pCur->pLeft = pCur->pRight = NULL;
253             }
254             return pCur;
255         }
256         else
257         {
258             if( nRes < 0 )
259                 return Rem( &pCur->pLeft, pDel, bPtrs );
260             else
261                 return Rem( &pCur->pRight, pDel, bPtrs );
262         }
263     }
264     return NULL;
265 }
266 
267 // Enumerate the tree for later iteration
268 
269 void StgAvlNode::StgEnum( short& n )
270 {
271     if( this )
272     {
273         if( pLeft )
274             pLeft->StgEnum( n );
275         nId = n++;
276         if( pRight )
277             pRight->StgEnum( n );
278     }
279 }
280 
281 // Add node to AVL tree.
282 // Return sal_False if the element already exists.
283 
284 sal_Bool StgAvlNode::Insert( StgAvlNode** pRoot, StgAvlNode* pIns )
285 {
286     StgAvlNode* pPivot, *pHeavy, *pNewRoot, *pParent, *pPrev;
287     // special case - empty tree
288     if( *pRoot == NULL )
289     {
290         *pRoot = pIns;
291         return sal_True;
292     }
293     // find insertion point and return if already present
294     short nRes = (*pRoot)->Locate( pIns, &pPivot, &pParent, &pPrev );
295     if( !nRes )
296         return sal_False;
297     // add new node
298     if( nRes < 0 )
299         pPrev->pLeft = pIns;
300     else
301         pPrev->pRight = pIns;
302     // rebalance tree
303     short nDelta = pPivot->Adjust( &pHeavy, pIns );
304     if( pPivot->nBalance >= 2 || pPivot->nBalance <= -2 )
305     {
306         pHeavy = ( nDelta < 0 ) ? pPivot->pRight : pPivot->pLeft;
307         // left imbalance
308         if( nDelta > 0 )
309             if( pHeavy->nBalance == 1 )
310                 pNewRoot = pPivot->RotLL();
311             else
312                 pNewRoot = pPivot->RotLR();
313         // right imbalance
314         else if( pHeavy->nBalance == -1 )
315             pNewRoot = pPivot->RotRR();
316         else
317             pNewRoot = pPivot->RotRL();
318         // relink balanced subtree
319         if( pParent == NULL )
320             *pRoot = pNewRoot;
321         else if( pPivot == pParent->pLeft )
322             pParent->pLeft = pNewRoot;
323         else if( pPivot == pParent->pRight )
324             pParent->pRight = pNewRoot;
325     }
326     return sal_True;
327 }
328 
329 // Remove node from tree. Returns sal_True is found and removed.
330 // Actually delete if bDel
331 
332 sal_Bool StgAvlNode::Remove( StgAvlNode** pRoot, StgAvlNode* pDel, sal_Bool bDel )
333 {
334     // special case - empty tree
335     if( *pRoot == NULL )
336         return sal_False;
337     // delete the element
338     pDel = Rem( pRoot, pDel, sal_False );
339 	if( pDel )
340 	{
341 		if( bDel )
342 			delete pDel;
343 		// Rebalance the tree the hard way
344 		// OS 22.09.95: Auf MD's Wunsch auskommentiert wg. Absturz
345 /*		StgAvlNode* pNew = NULL;
346 		while( *pRoot )
347 		{
348 			StgAvlNode* p = Rem( pRoot, *pRoot, sal_False );
349 			Insert( &pNew, p );
350 		}
351 		*pRoot = pNew;*/
352 		return sal_True;
353 	}
354 	else
355 		return sal_False;
356 }
357 
358 // Move node to a different tree. Returns sal_True is found and moved. This routine
359 // may be called when the key has changed.
360 
361 sal_Bool StgAvlNode::Move
362 	( StgAvlNode** pRoot1, StgAvlNode** pRoot2, StgAvlNode* pMove )
363 {
364     // special case - empty tree
365     if( *pRoot1 == NULL )
366         return sal_False;
367 	pMove = Rem( pRoot1, pMove, sal_False );
368 	if( pMove )
369 		return Insert( pRoot2, pMove );
370 	else
371 		return sal_False;
372 }
373 
374 ////////////////////////// class AvlIterator /////////////////////////
375 
376 // The iterator walks through a tree one entry by one.
377 
378 StgAvlIterator::StgAvlIterator( StgAvlNode* p )
379 {
380     pRoot = p;
381     nCount = 0;
382 	if( p )
383 	    p->StgEnum( nCount );
384 }
385 
386 StgAvlNode* StgAvlIterator::Find( short n )
387 {
388     StgAvlNode* p = pRoot;
389     while( p )
390     {
391         if( n == p->nId )
392             break;
393         else p = ( n < p->nId ) ? p->pLeft : p->pRight;
394     }
395     return p;
396 }
397 
398 StgAvlNode* StgAvlIterator::First()
399 {
400     nCur = -1;
401     return Next();
402 }
403 
404 StgAvlNode* StgAvlIterator::Last()
405 {
406     nCur = nCount;
407     return Prev();
408 }
409 
410 StgAvlNode* StgAvlIterator::Next()
411 {
412     return Find( ++nCur );
413 }
414 
415 StgAvlNode* StgAvlIterator::Prev()
416 {
417     return Find( --nCur );
418 }
419 
420