001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License.
018 *
019 */
020package org.apache.directory.mavibot.btree;
021
022
023import java.io.IOException;
024import java.util.NoSuchElementException;
025
026import org.apache.directory.mavibot.btree.exception.EndOfFileExceededException;
027
028
029/**
030 * A Cursor is used to fetch only keys in a BTree and is returned by the
031 * @see BTree#browseKeys method. The cursor <strng>must</strong> be closed
032 * when the user is done with it.
033 * <p>
034 *
035 * @param <K> The type for the Key
036 *
037 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
038 */
039public class KeyCursor<K>
040{
041    /** A marker to tell that we are before the first element */
042    private static final int BEFORE_FIRST = -1;
043
044    /** A marker to tell that we are after the last element */
045    private static final int AFTER_LAST = -2;
046
047    /** The stack of pages from the root down to the leaf */
048    protected ParentPos<K, K>[] stack;
049
050    /** The stack's depth */
051    protected int depth = 0;
052
053    /** The transaction used for this cursor */
054    protected ReadTransaction<K, K> transaction;
055
056
057    /**
058     * Creates a new instance of Cursor.
059     */
060    protected KeyCursor()
061    {
062    }
063
064
065    /**
066     * Creates a new instance of Cursor, starting on a page at a given position.
067     *
068     * @param transaction The transaction this operation is protected by
069     * @param stack The stack of parent's from root to this page
070     */
071    public KeyCursor( ReadTransaction<K, K> transaction, ParentPos<K, K>[] stack, int depth )
072    {
073        this.transaction = transaction;
074        this.stack = stack;
075        this.depth = depth;
076    }
077
078
079    /**
080     * Change the position in the current cursor to set it after the last key
081     */
082    public void afterLast() throws IOException
083    {
084        // First check that we have elements in the BTree
085        if ( ( stack == null ) || ( stack.length == 0 ) )
086        {
087            return;
088        }
089
090        Page<K, K> child = null;
091
092        for ( int i = 0; i < depth; i++ )
093        {
094            ParentPos<K, K> parentPos = stack[i];
095
096            if ( child != null )
097            {
098                parentPos.page = child;
099                parentPos.pos = child.getNbElems();
100            }
101            else
102            {
103                // We have N+1 children if the page is a Node, so we don't decrement the nbElems field
104                parentPos.pos = parentPos.page.getNbElems();
105            }
106
107            child = ( ( AbstractPage<K, K> ) parentPos.page ).getPage( parentPos.pos );
108        }
109
110        // and leaf
111        ParentPos<K, K> parentPos = stack[depth];
112
113        if ( child == null )
114        {
115            parentPos.pos = parentPos.page.getNbElems() - 1;
116        }
117        else
118        {
119            parentPos.page = child;
120            parentPos.pos = child.getNbElems() - 1;
121        }
122
123        parentPos.pos = AFTER_LAST;
124    }
125
126
127    /**
128     * Change the position in the current cursor before the first key
129     */
130    public void beforeFirst() throws IOException
131    {
132        // First check that we have elements in the BTree
133        if ( ( stack == null ) || ( stack.length == 0 ) )
134        {
135            return;
136        }
137
138        Page<K, K> child = null;
139
140        for ( int i = 0; i < depth; i++ )
141        {
142            ParentPos<K, K> parentPos = stack[i];
143            parentPos.pos = 0;
144
145            if ( child != null )
146            {
147                parentPos.page = child;
148            }
149
150            child = ( ( AbstractPage<K, K> ) parentPos.page ).getPage( 0 );
151        }
152
153        // and leaf
154        ParentPos<K, K> parentPos = stack[depth];
155        parentPos.pos = BEFORE_FIRST;
156
157        if ( child != null )
158        {
159            parentPos.page = child;
160        }
161    }
162
163
164    /**
165     * Tells if the cursor can return a next element
166     *
167     * @return true if there are some more elements
168     * @throws IOException
169     * @throws EndOfFileExceededException
170     */
171    public boolean hasNext() throws EndOfFileExceededException, IOException
172    {
173        // First check that we have elements in the BTree
174        if ( ( stack == null ) || ( stack.length == 0 ) )
175        {
176            return false;
177        }
178
179        // Take the leaf and check if we have no mare keys
180        ParentPos<K, K> parentPos = stack[depth];
181
182        if ( parentPos.page == null )
183        {
184            // Empty BTree, get out
185            return false;
186        }
187
188        if ( parentPos.pos == AFTER_LAST )
189        {
190            return false;
191        }
192
193        if ( parentPos.pos == BEFORE_FIRST )
194        {
195            return true;
196        }
197
198        if ( parentPos.pos < parentPos.page.getNbElems() - 1 )
199        {
200            // Not the last position, we have a next key
201            return true;
202        }
203        else
204        {
205            // Ok, here, we have reached the last key in the leaf. We have to go up and
206            // see if we have some remaining keys
207            int currentDepth = depth - 1;
208
209            while ( currentDepth >= 0 )
210            {
211                parentPos = stack[currentDepth];
212
213                if ( parentPos.pos < parentPos.page.getNbElems() )
214                {
215                    // The parent has some remaining keys on the right, get out
216                    return true;
217                }
218                else
219                {
220                    currentDepth--;
221                }
222            }
223
224            // We are done, there are no more key left
225            return false;
226        }
227    }
228
229
230    /**
231     * Find the next key
232     *
233     * @return the found key
234     * @throws IOException
235     * @throws EndOfFileExceededException
236     */
237    public K next() throws EndOfFileExceededException, IOException
238    {
239        // First check that we have elements in the BTree
240        if ( ( stack == null ) || ( stack.length == 0 ) )
241        {
242            throw new NoSuchElementException( "No Key is present" );
243        }
244
245        ParentPos<K, K> parentPos = stack[depth];
246
247        if ( ( parentPos.page == null ) || ( parentPos.pos == AFTER_LAST ) )
248        {
249            // This is the end : no more keys
250            throw new NoSuchElementException( "No more keys present" );
251        }
252
253        if ( parentPos.pos == parentPos.page.getNbElems() )
254        {
255            // End of the leaf. We have to go back into the stack up to the
256            // parent, and down to the leaf
257            parentPos = findNextParentPos();
258
259            // we also need to check for the type of page cause
260            // findNextParentPos will never return a null ParentPos
261            if ( ( parentPos == null ) || ( parentPos.page == null ) )
262            {
263                // This is the end : no more keys
264                throw new NoSuchElementException( "No more keys present" );
265            }
266        }
267
268        // Deal with the BeforeFirst case
269        if ( parentPos.pos == BEFORE_FIRST )
270        {
271            parentPos.pos++;
272        }
273        else
274        {
275            if ( parentPos.pos == parentPos.page.getNbElems() - 1 )
276            {
277                parentPos = findNextParentPos();
278                
279                if ( ( parentPos == null ) || ( parentPos.page == null ) )
280                {
281                    // This is the end : no more keys
282                    throw new NoSuchElementException( "No more keys present" );
283                }
284            }
285            else
286            {
287                parentPos.pos++;
288            }
289        }
290        
291        AbstractPage<K, K> leaf = ( AbstractPage<K, K> ) ( parentPos.page );
292
293        return leaf.getKey( parentPos.pos );
294    }
295
296
297    /**
298     * Get the next key.
299     */
300    public K nextKey() throws EndOfFileExceededException, IOException
301    {
302        return next();
303    }
304
305
306    /**
307     * Tells if the cursor can return a next key
308     *
309     * @return true if there are some more keys
310     * @throws IOException
311     * @throws EndOfFileExceededException
312     */
313    public boolean hasNextKey() throws EndOfFileExceededException, IOException
314    {
315        // First check that we have elements in the BTree
316        if ( ( stack == null ) || ( stack.length == 0 ) )
317        {
318            // This is the end : no more key
319            return false;
320        }
321
322        ParentPos<K, K> parentPos = stack[depth];
323
324        if ( parentPos.page == null )
325        {
326            // This is the end : no more key
327            return false;
328        }
329
330        if ( parentPos.pos == ( parentPos.page.getNbElems() - 1 ) )
331        {
332            // End of the leaf. We have to go back into the stack up to the
333            // parent, and down to the next leaf
334            return hasNextParentPos();
335        }
336        else
337        {
338            return true;
339        }
340    }
341
342
343    /**
344     * Tells if the cursor can return a previous element
345     *
346     * @return true if there are some more elements
347     * @throws IOException
348     * @throws EndOfFileExceededException
349     */
350    public boolean hasPrev() throws EndOfFileExceededException, IOException
351    {
352        // First check that we have elements in the BTree
353        if ( ( stack == null ) || ( stack.length == 0 ) )
354        {
355            return false;
356        }
357
358        // Take the leaf and check if we have no mare keys
359        ParentPos<K, K> parentPos = stack[depth];
360
361        if ( parentPos.page == null )
362        {
363            // Empty BTree, get out
364            return false;
365        }
366
367        if ( parentPos.pos > 0 )
368        {
369            // get out, we have keys on the left
370            return true;
371        }
372        else
373        {
374            // Check that we are not before the first key
375            if ( parentPos.pos == BEFORE_FIRST )
376            {
377                return false;
378            }
379
380            if ( parentPos.pos == AFTER_LAST )
381            {
382                return true;
383            }
384
385            // Ok, here, we have reached the first key in the leaf. We have to go up and
386            // see if we have some remaining keys
387            int currentDepth = depth - 1;
388
389            while ( currentDepth >= 0 )
390            {
391                parentPos = stack[currentDepth];
392
393                if ( parentPos.pos > 0 )
394                {
395                    // The parent has some remaining keys on the right, get out
396                    return true;
397                }
398                else
399                {
400                    currentDepth--;
401                }
402            }
403
404            return false;
405        }
406    }
407
408
409    /**
410     * Find the previous key
411     *
412     * @return the found key
413     * @throws IOException
414     * @throws EndOfFileExceededException
415     */
416    public K prev() throws EndOfFileExceededException, IOException
417    {
418        // First check that we have elements in the BTree
419        if ( ( stack == null ) || ( stack.length == 0 ) )
420        {
421            throw new NoSuchElementException( "No more keys present" );
422        }
423
424        ParentPos<K, K> parentPos = stack[depth];
425
426        if ( ( parentPos.page == null ) || ( parentPos.pos == BEFORE_FIRST ) )
427        {
428            // This is the end : no more keys
429            throw new NoSuchElementException( "No more keys present" );
430        }
431
432        // Deal with the AfterLast case
433        if ( parentPos.pos == AFTER_LAST )
434        {
435            parentPos.pos = parentPos.page.getNbElems() - 1;
436        }
437        else
438        {
439            if ( parentPos.pos == 0 )
440            {
441                parentPos = findPrevParentPos();
442                
443                if ( ( parentPos == null ) || ( parentPos.page == null ) )
444                {
445                    // This is the end : no more keys
446                    throw new NoSuchElementException( "No more keys present" );
447                }
448            }
449            else
450            {
451                parentPos.pos--;
452            }
453        }
454
455        AbstractPage<K, K> leaf = ( AbstractPage<K, K> ) ( parentPos.page );
456
457        return leaf.getKey( parentPos.pos );
458    }
459
460
461    /**
462     * Get the previous key.
463     * 
464     * @return the found key
465     * @throws EndOfFileExceededException
466     * @throws IOException
467     */
468    public K prevKey() throws EndOfFileExceededException, IOException
469    {
470        return prev();
471    }
472
473
474    /**
475     * Tells if the cursor can return a previous key
476     *
477     * @return true if there are some more keys
478     * @throws IOException
479     * @throws EndOfFileExceededException
480     */
481    public boolean hasPrevKey() throws EndOfFileExceededException, IOException
482    {
483        // First check that we have elements in the BTree
484        if ( ( stack == null ) || ( stack.length == 0 ) )
485        {
486            // This is the end : no more key
487            return false;
488        }
489
490        ParentPos<K, K> parentPos = stack[depth];
491
492        if ( parentPos.page == null )
493        {
494            // This is the end : no more key
495            return false;
496        }
497
498        switch ( parentPos.pos )
499        {
500            case 0 :
501                // Beginning of the leaf. We have to go back into the stack up to the
502                // parent, and down to the leaf
503                return hasPrevParentPos();
504                
505            case -1 :
506                // no previous key
507                return false;
508                
509            default :
510                // we have a previous key
511                return true;
512        }
513    }
514
515
516    /**
517     * Tells if there is a next ParentPos
518     *
519     * @return the new ParentPos instance, or null if we have no following leaf
520     * @throws IOException
521     * @throws EndOfFileExceededException
522     */
523    private boolean hasNextParentPos() throws EndOfFileExceededException, IOException
524    {
525        if ( depth == 0 )
526        {
527            // No need to go any further, there is only one leaf in the btree
528            return false;
529        }
530
531        int currentDepth = depth - 1;
532        Page<K, K> child = null;
533
534        // First, go up the tree until we find a Node which has some element on the right
535        while ( currentDepth >= 0 )
536        {
537            // We first go up the tree, until we reach a page whose current position
538            // is not the last one
539            ParentPos<K, K> parentPos = stack[currentDepth];
540
541            if ( parentPos.pos + 1 > parentPos.page.getNbElems() )
542            {
543                // No more element on the right : go up
544                currentDepth--;
545            }
546            else
547            {
548                // We can pick the next element at this level
549                child = ( ( AbstractPage<K, K> ) parentPos.page ).getPage( parentPos.pos + 1 );
550
551                // and go down the tree through the nodes
552                while ( currentDepth < depth - 1 )
553                {
554                    currentDepth++;
555                    child = ( ( AbstractPage<K, K> ) child ).getPage( 0 );
556                }
557
558                return true;
559            }
560        }
561
562        return false;
563    }
564
565
566    /**
567     * Find the leaf containing the following elements.
568     *
569     * @return the new ParentPos instance, or null if we have no following leaf
570     * @throws IOException
571     * @throws EndOfFileExceededException
572     */
573    private ParentPos<K, K> findNextParentPos() throws EndOfFileExceededException, IOException
574    {
575        if ( depth == 0 )
576        {
577            // No need to go any further, there is only one leaf in the btree
578            return null;
579        }
580
581        int currentDepth = depth - 1;
582        Page<K, K> child = null;
583
584        // First, go up the tree until we find a Node which has some element on the right
585        while ( currentDepth >= 0 )
586        {
587            // We first go up the tree, until we reach a page whose current position
588            // is not the last one
589            ParentPos<K, K> parentPos = stack[currentDepth];
590
591            if ( parentPos.pos + 1 > parentPos.page.getNbElems() )
592            {
593                // No more element on the right : go up
594                currentDepth--;
595            }
596            else
597            {
598                // We can pick the next element at this level
599                parentPos.pos++;
600                child = ( ( AbstractPage<K, K> ) parentPos.page ).getPage( parentPos.pos );
601
602                // and go down the tree through the nodes
603                while ( currentDepth < depth - 1 )
604                {
605                    currentDepth++;
606                    parentPos = stack[currentDepth];
607                    parentPos.pos = 0;
608                    parentPos.page = child;
609                    child = ( ( AbstractPage<K, K> ) child ).getPage( 0 );
610                }
611
612                // and the leaf
613                parentPos = stack[depth];
614                parentPos.page = child;
615                parentPos.pos = 0;
616
617                return parentPos;
618            }
619        }
620
621        return null;
622    }
623
624
625    /**
626     * Find the leaf containing the previous elements.
627     *
628     * @return the new ParentPos instance, or null if we have no previous leaf
629     * @throws IOException
630     * @throws EndOfFileExceededException
631     */
632    private ParentPos<K, K> findPrevParentPos() throws EndOfFileExceededException, IOException
633    {
634        if ( depth == 0 )
635        {
636            // No need to go any further, there is only one leaf in the btree
637            return null;
638        }
639
640        int currentDepth = depth - 1;
641        Page<K, K> child = null;
642
643        // First, go up the tree until we find a Node which has some element on the left
644        while ( currentDepth >= 0 )
645        {
646            // We first go up the tree, until we reach a page whose current position
647            // is not the last one
648            ParentPos<K, K> parentPos = stack[currentDepth];
649
650            if ( parentPos.pos == 0 )
651            {
652                // No more element on the right : go up
653                currentDepth--;
654            }
655            else
656            {
657                // We can pick the next element at this level
658                parentPos.pos--;
659                child = ( ( AbstractPage<K, K> ) parentPos.page ).getPage( parentPos.pos );
660
661                // and go down the tree through the nodes
662                while ( currentDepth < depth - 1 )
663                {
664                    currentDepth++;
665                    parentPos = stack[currentDepth];
666                    parentPos.pos = child.getNbElems();
667                    parentPos.page = child;
668                    child = ( ( AbstractPage<K, K> ) parentPos.page ).getPage( parentPos.page.getNbElems() );
669                }
670
671                // and the leaf
672                parentPos = stack[depth];
673                parentPos.pos = child.getNbElems() - 1;
674                parentPos.page = child;
675
676                return parentPos;
677            }
678        }
679
680        return null;
681    }
682
683
684    /**
685     * Tells if there is a prev ParentPos
686     *
687     * @return the new ParentPos instance, or null if we have no previous leaf
688     * @throws IOException
689     * @throws EndOfFileExceededException
690     */
691    private boolean hasPrevParentPos() throws EndOfFileExceededException, IOException
692    {
693        if ( depth == 0 )
694        {
695            // No need to go any further, there is only one leaf in the btree
696            return false;
697        }
698
699        int currentDepth = depth - 1;
700        Page<K, K> child = null;
701
702        // First, go up the tree until we find a Node which has some element on the right
703        while ( currentDepth >= 0 )
704        {
705            // We first go up the tree, until we reach a page whose current position
706            // is not the last one
707            ParentPos<K, K> parentPos = stack[currentDepth];
708
709            if ( parentPos.pos == 0 )
710            {
711                // No more element on the left : go up
712                currentDepth--;
713            }
714            else
715            {
716                // We can pick the previous element at this level
717                child = ( ( AbstractPage<K, K> ) parentPos.page ).getPage( parentPos.pos - 1 );
718
719                // and go down the tree through the nodes
720                while ( currentDepth < depth - 1 )
721                {
722                    currentDepth++;
723                    child = ( ( AbstractPage<K, K> ) child ).getPage( child.getNbElems() );
724                }
725
726                return true;
727            }
728        }
729
730        return false;
731    }
732
733
734    /**
735     * {@inheritDoc}
736     */
737    public void close()
738    {
739        transaction.close();
740    }
741
742
743    /**
744     * Get the creation date
745     * @return The creation date for this cursor
746     */
747    public long getCreationDate()
748    {
749        return transaction.getCreationDate();
750    }
751
752
753    /**
754     * Get the current revision
755     *
756     * @return The revision this cursor is based on
757     */
758    public long getRevision()
759    {
760        return transaction.getRevision();
761    }
762
763
764    public String toString()
765    {
766        StringBuilder sb = new StringBuilder();
767
768        sb.append( "KeyCursor, depth = " ).append( depth ).append( "\n" );
769
770        for ( int i = 0; i <= depth; i++ )
771        {
772            sb.append( "    " ).append( stack[i] ).append( "\n" );
773        }
774
775        return sb.toString();
776    }
777}