View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   *
19   */
20  package org.apache.directory.mavibot.btree;
21  
22  
23  import java.io.IOException;
24  import java.util.NoSuchElementException;
25  
26  import org.apache.directory.mavibot.btree.exception.EndOfFileExceededException;
27  
28  
29  /**
30   * A Cursor is used to fetch elements in a BTree and is returned by the
31   * @see BTree#browse method. The cursor <strng>must</strong> be closed
32   * when the user is done with it.
33   * <p>
34   *
35   * @param <K> The type for the Key
36   * @param <V> The type for the stored value
37   *
38   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
39   */
40  public class TupleCursor<K, V>
41  {
42      /** A marker to tell that we are before the first element */
43      private static final int BEFORE_FIRST = -1;
44  
45      /** A marker to tell that we are after the last element */
46      private static final int AFTER_LAST = -2;
47  
48      /** The stack of pages from the root down to the leaf */
49      protected ParentPos<K, V>[] stack;
50  
51      /** The stack's depth */
52      protected int depth = 0;
53  
54      /** The transaction used for this cursor */
55      protected ReadTransaction<K, V> transaction;
56  
57  
58      /**
59       * Creates a new instance of Cursor.
60       */
61      protected TupleCursor()
62      {
63      }
64  
65  
66      /**
67       * Creates a new instance of Cursor, starting on a page at a given position.
68       *
69       * @param transaction The transaction this operation is protected by
70       * @param stack The stack of parent's from root to this page
71       */
72      public TupleCursor( ReadTransaction<K, V> transaction, ParentPos<K, V>[] stack, int depth )
73      {
74          this.transaction = transaction;
75          this.stack = stack;
76          this.depth = depth;
77      }
78  
79  
80      /**
81       * Change the position in the current cursor to set it after the last key
82       */
83      public void afterLast() throws IOException
84      {
85          // First check that we have elements in the BTree
86          if ( ( stack == null ) || ( stack.length == 0 ) )
87          {
88              return;
89          }
90  
91          Page<K, V> child = null;
92  
93          for ( int i = 0; i < depth; i++ )
94          {
95              ParentPos<K, V> parentPos = stack[i];
96  
97              if ( child != null )
98              {
99                  parentPos.page = child;
100                 parentPos.pos = child.getNbElems();
101             }
102             else
103             {
104                 // We have N+1 children if the page is a Node, so we don't decrement the nbElems field
105                 parentPos.pos = parentPos.page.getNbElems();
106             }
107 
108             child = ( ( AbstractPage<K, V> ) parentPos.page ).getPage( parentPos.pos );
109         }
110 
111         // and leaf
112         ParentPos<K, V> parentPos = stack[depth];
113 
114         if ( child == null )
115         {
116             parentPos.pos = parentPos.page.getNbElems() - 1;
117         }
118         else
119         {
120             parentPos.page = child;
121             parentPos.pos = child.getNbElems() - 1;
122         }
123 
124         parentPos.valueCursor = ( ( AbstractPage<K, V> ) parentPos.page ).getValue( parentPos.pos ).getCursor();
125         parentPos.valueCursor.afterLast();
126         parentPos.pos = AFTER_LAST;
127     }
128 
129 
130     /**
131      * Change the position in the current cursor before the first key
132      */
133     public void beforeFirst() throws IOException
134     {
135         // First check that we have elements in the BTree
136         if ( ( stack == null ) || ( stack.length == 0 ) )
137         {
138             return;
139         }
140 
141         Page<K, V> child = null;
142 
143         for ( int i = 0; i < depth; i++ )
144         {
145             ParentPos<K, V> parentPos = stack[i];
146             parentPos.pos = 0;
147 
148             if ( child != null )
149             {
150                 parentPos.page = child;
151             }
152 
153             child = ( ( AbstractPage<K, V> ) parentPos.page ).getPage( 0 );
154         }
155 
156         // and leaf
157         ParentPos<K, V> parentPos = stack[depth];
158         parentPos.pos = BEFORE_FIRST;
159 
160         if ( child != null )
161         {
162             parentPos.page = child;
163         }
164 
165         if ( parentPos.valueCursor != null )
166         {
167             parentPos.valueCursor = ( ( AbstractPage<K, V> ) parentPos.page ).getValue( 0 ).getCursor();
168             parentPos.valueCursor.beforeFirst();
169         }
170     }
171 
172 
173     /**
174      * Tells if the cursor can return a next element
175      *
176      * @return true if there are some more elements
177      * @throws IOException
178      * @throws EndOfFileExceededException
179      */
180     public boolean hasNext() throws EndOfFileExceededException, IOException
181     {
182         // First check that we have elements in the BTree
183         if ( ( stack == null ) || ( stack.length == 0 ) )
184         {
185             return false;
186         }
187 
188         // Take the leaf and check if we have no mare values
189         ParentPos<K, V> parentPos = stack[depth];
190 
191         if ( parentPos.page == null )
192         {
193             // Empty BTree, get out
194             return false;
195         }
196 
197         if ( parentPos.pos == AFTER_LAST )
198         {
199             return false;
200         }
201 
202         if ( parentPos.pos < parentPos.page.getNbElems() - 1 )
203         {
204             // Not the last position, we have a next value
205             return true;
206         }
207         else
208         {
209             // Check if we have some more value
210             if ( parentPos.valueCursor.hasNext() )
211             {
212                 return true;
213             }
214 
215             // Ok, here, we have reached the last value in the leaf. We have to go up and
216             // see if we have some remaining values
217             int currentDepth = depth - 1;
218 
219             while ( currentDepth >= 0 )
220             {
221                 parentPos = stack[currentDepth];
222 
223                 if ( parentPos.pos < parentPos.page.getNbElems() )
224                 {
225                     // The parent has some remaining values on the right, get out
226                     return true;
227                 }
228                 else
229                 {
230                     currentDepth--;
231                 }
232             }
233 
234             // We are done, there are no more value left
235             return false;
236         }
237     }
238 
239 
240     /**
241      * Find the next key/value
242      *
243      * @return A Tuple containing the found key and value
244      * @throws IOException
245      * @throws EndOfFileExceededException
246      */
247     public Tuple<K, V> next() throws EndOfFileExceededException, IOException
248     {
249         // First check that we have elements in the BTree
250         if ( ( stack == null ) || ( stack.length == 0 ) )
251         {
252             throw new NoSuchElementException( "No tuple present" );
253         }
254 
255         ParentPos<K, V> parentPos = stack[depth];
256 
257         if ( ( parentPos.page == null ) || ( parentPos.pos == AFTER_LAST ) )
258         {
259             // This is the end : no more value
260             throw new NoSuchElementException( "No more tuples present" );
261         }
262 
263         if ( parentPos.pos == parentPos.page.getNbElems() )
264         {
265             // End of the leaf. We have to go back into the stack up to the
266             // parent, and down to the leaf
267             parentPos = findNextParentPos();
268 
269             // we also need to check for the type of page cause
270             // findNextParentPos will never return a null ParentPos
271             if ( ( parentPos == null ) || ( parentPos.page == null ) )
272             {
273                 // This is the end : no more value
274                 throw new NoSuchElementException( "No more tuples present" );
275             }
276         }
277 
278         V value = null;
279 
280         if ( parentPos.valueCursor.hasNext() )
281         {
282             // Deal with the BeforeFirst case
283             if ( parentPos.pos == BEFORE_FIRST )
284             {
285                 parentPos.pos++;
286             }
287 
288             value = parentPos.valueCursor.next();
289         }
290         else
291         {
292             if ( parentPos.pos == parentPos.page.getNbElems() - 1 )
293             {
294                 parentPos = findNextParentPos();
295 
296                 if ( ( parentPos == null ) || ( parentPos.page == null ) )
297                 {
298                     // This is the end : no more value
299                     throw new NoSuchElementException( "No more tuples present" );
300                 }
301             }
302             else
303             {
304                 parentPos.pos++;
305             }
306 
307             try
308             {
309                 ValueHolder<V> valueHolder = ( ( AbstractPage<K, V> ) parentPos.page ).getValue( parentPos.pos );
310 
311                 parentPos.valueCursor = valueHolder.getCursor();
312 
313                 value = parentPos.valueCursor.next();
314             }
315             catch ( IllegalArgumentException e )
316             {
317                 e.printStackTrace();
318             }
319         }
320 
321         AbstractPage<K, V> leaf = ( AbstractPage<K, V> ) ( parentPos.page );
322         Tuple<K, V> tuple = new Tuple<K, V>( leaf.getKey( parentPos.pos ), value );
323 
324         return tuple;
325     }
326 
327 
328     /**
329      * Get the next non-duplicate key.
330      * If the BTree contains :
331      *
332      *  <ul>
333      *    <li><1,0></li>
334      *    <li><1,1></li>
335      *    <li><1,2></li>
336      *    <li><2,0></li>
337      *    <li><2,1></li>
338      *  </ul>
339      *
340      *  and cursor is present at <1,1> then the returned tuple will be <2,0> (not <1,2>)
341      *
342      * @return A Tuple containing the found key and value
343      * @throws EndOfFileExceededException
344      * @throws IOException
345      */
346     public Tuple<K, V> nextKey() throws EndOfFileExceededException, IOException
347     {
348         // First check that we have elements in the BTree
349         if ( ( stack == null ) || ( stack.length == 0 ) )
350         {
351             // This is the end : no more value
352             throw new NoSuchElementException( "No more tuples present" );
353         }
354 
355         ParentPos<K, V> parentPos = stack[depth];
356 
357         if ( parentPos.page == null )
358         {
359             // This is the end : no more value
360             throw new NoSuchElementException( "No more tuples present" );
361         }
362 
363         if ( parentPos.pos == ( parentPos.page.getNbElems() - 1 ) )
364         {
365             // End of the leaf. We have to go back into the stack up to the
366             // parent, and down to the next leaf
367             ParentPos<K, V> newParentPos = findNextParentPos();
368 
369             // we also need to check the result of the call to
370             // findNextParentPos as it will return a null ParentPos
371             if ( ( newParentPos == null ) || ( newParentPos.page == null ) )
372             {
373                 // This is the end : no more value
374                 AbstractPage<K, V> leaf = ( AbstractPage<K, V> ) ( parentPos.page );
375                 ValueHolder<V> valueHolder = leaf.getValue( parentPos.pos );
376                 parentPos.pos = AFTER_LAST;
377                 parentPos.valueCursor = valueHolder.getCursor();
378                 parentPos.valueCursor.afterLast();
379 
380                 return null;
381             }
382             else
383             {
384                 parentPos = newParentPos;
385             }
386         }
387         else
388         {
389             // Get the next key
390             parentPos.pos++;
391         }
392 
393         // The key
394         AbstractPage<K, V> leaf = ( AbstractPage<K, V> ) ( parentPos.page );
395         Tuple<K, V> tuple = new Tuple<K, V>();
396         tuple.setKey( leaf.getKey( parentPos.pos ) );
397 
398         // The value
399         ValueHolder<V> valueHolder = leaf.getValue( parentPos.pos );
400         parentPos.valueCursor = valueHolder.getCursor();
401         V value = parentPos.valueCursor.next();
402         tuple.setValue( value );
403 
404         return tuple;
405     }
406 
407 
408     /**
409      * Tells if the cursor can return a next key
410      *
411      * @return true if there are some more keys
412      * @throws IOException
413      * @throws EndOfFileExceededException
414      */
415     public boolean hasNextKey() throws EndOfFileExceededException, IOException
416     {
417         // First check that we have elements in the BTree
418         if ( ( stack == null ) || ( stack.length == 0 ) )
419         {
420             // This is the end : no more key
421             return false;
422         }
423 
424         ParentPos<K, V> parentPos = stack[depth];
425 
426         if ( parentPos.page == null )
427         {
428             // This is the end : no more key
429             return false;
430         }
431 
432         if ( parentPos.pos == ( parentPos.page.getNbElems() - 1 ) )
433         {
434             // End of the leaf. We have to go back into the stack up to the
435             // parent, and down to the next leaf
436             return hasNextParentPos();
437         }
438         else
439         {
440             return true;
441         }
442     }
443 
444 
445     /**
446      * Tells if the cursor can return a previous element
447      *
448      * @return true if there are some more elements
449      * @throws IOException
450      * @throws EndOfFileExceededException
451      */
452     public boolean hasPrev() throws EndOfFileExceededException, IOException
453     {
454         // First check that we have elements in the BTree
455         if ( ( stack == null ) || ( stack.length == 0 ) )
456         {
457             return false;
458         }
459 
460         // Take the leaf and check if we have no mare values
461         ParentPos<K, V> parentPos = stack[depth];
462 
463         if ( parentPos.page == null )
464         {
465             // Empty BTree, get out
466             return false;
467         }
468 
469         if ( parentPos.pos > 0 )
470         {
471             // get out, we have values on the left
472             return true;
473         }
474         else
475         {
476             // Check that we are not before the first value
477             if ( parentPos.pos == BEFORE_FIRST )
478             {
479                 return false;
480             }
481 
482             // Check if we have some more value
483             if ( parentPos.valueCursor.hasPrev() )
484             {
485                 return true;
486             }
487 
488             // Ok, here, we have reached the first value in the leaf. We have to go up and
489             // see if we have some remaining values
490             int currentDepth = depth - 1;
491 
492             while ( currentDepth >= 0 )
493             {
494                 parentPos = stack[currentDepth];
495 
496                 if ( parentPos.pos > 0 )
497                 {
498                     // The parent has some remaining values on the right, get out
499                     return true;
500                 }
501                 else
502                 {
503                     currentDepth--;
504                 }
505             }
506 
507             return false;
508         }
509     }
510 
511 
512     /**
513      * Find the previous key/value
514      *
515      * @return A Tuple containing the found key and value
516      * @throws IOException
517      * @throws EndOfFileExceededException
518      */
519     public Tuple<K, V> prev() throws EndOfFileExceededException, IOException
520     {
521         // First check that we have elements in the BTree
522         if ( ( stack == null ) || ( stack.length == 0 ) )
523         {
524             throw new NoSuchElementException( "No more tuple present" );
525         }
526 
527         ParentPos<K, V> parentPos = stack[depth];
528 
529         if ( ( parentPos.page == null ) || ( parentPos.pos == BEFORE_FIRST ) )
530         {
531             // This is the end : no more value
532             throw new NoSuchElementException( "No more tuples present" );
533         }
534 
535         if ( ( parentPos.pos == 0 ) && ( !parentPos.valueCursor.hasPrev() ) )
536         {
537             // End of the leaf. We have to go back into the stack up to the
538             // parent, and down to the leaf
539             parentPos = findPrevParentPos();
540 
541             // we also need to check for the type of page cause
542             // findPrevParentPos will never return a null ParentPos
543             if ( ( parentPos == null ) || ( parentPos.page == null ) )
544             {
545                 // This is the end : no more value
546                 throw new NoSuchElementException( "No more tuples present" );
547             }
548         }
549 
550         V value = null;
551 
552         if ( parentPos.valueCursor.hasPrev() )
553         {
554             // Deal with the AfterLast case
555             if ( parentPos.pos == AFTER_LAST )
556             {
557                 parentPos.pos = parentPos.page.getNbElems() - 1;
558             }
559 
560             value = parentPos.valueCursor.prev();
561         }
562         else
563         {
564             if ( parentPos.pos == 0 )
565             {
566                 parentPos = findPrevParentPos();
567 
568                 if ( ( parentPos == null ) || ( parentPos.page == null ) )
569                 {
570                     // This is the end : no more value
571                     throw new NoSuchElementException( "No more tuples present" );
572                 }
573             }
574             else
575             {
576                 parentPos.pos--;
577 
578                 try
579                 {
580                     ValueHolder<V> valueHolder = ( ( AbstractPage<K, V> ) parentPos.page ).getValue( parentPos.pos );
581 
582                     parentPos.valueCursor = valueHolder.getCursor();
583                     parentPos.valueCursor.afterLast();
584 
585                     value = parentPos.valueCursor.prev();
586                 }
587                 catch ( IllegalArgumentException e )
588                 {
589                     e.printStackTrace();
590                 }
591             }
592         }
593 
594         AbstractPage<K, V> leaf = ( AbstractPage<K, V> ) ( parentPos.page );
595         Tuple<K, V> tuple = new Tuple<K, V>( leaf.getKey( parentPos.pos ), value );
596 
597         return tuple;
598     }
599 
600 
601     /**
602      * Get the previous non-duplicate key.
603      * If the BTree contains :
604      *
605      *  <ul>
606      *    <li><1,0></li>
607      *    <li><1,1></li>
608      *    <li><1,2></li>
609      *    <li><2,0></li>
610      *    <li><2,1></li>
611      *  </ul>
612      *
613      *  and cursor is present at <2,1> then the returned tuple will be <1,0> (not <2,0>)
614      *
615      * @return A Tuple containing the found key and value
616      * @throws EndOfFileExceededException
617      * @throws IOException
618      */
619     public Tuple<K, V> prevKey() throws EndOfFileExceededException, IOException
620     {
621         // First check that we have elements in the BTree
622         if ( ( stack == null ) || ( stack.length == 0 ) )
623         {
624             // This is the end : no more value
625             throw new NoSuchElementException( "No more tuples present" );
626         }
627 
628         ParentPos<K, V> parentPos = stack[depth];
629 
630         if ( parentPos.page == null )
631         {
632             // This is the end : no more value
633             throw new NoSuchElementException( "No more tuples present" );
634         }
635 
636         if ( parentPos.pos == 0 )
637         {
638             // Beginning of the leaf. We have to go back into the stack up to the
639             // parent, and down to the leaf
640             parentPos = findPrevParentPos();
641 
642             if ( ( parentPos == null ) || ( parentPos.page == null ) )
643             {
644                 // This is the end : no more value
645                 throw new NoSuchElementException( "No more tuples present" );
646             }
647         }
648         else
649         {
650             if ( parentPos.pos == AFTER_LAST )
651             {
652                 parentPos.pos = parentPos.page.getNbElems() - 1;
653             }
654             else
655             {
656                 parentPos.pos--;
657             }
658         }
659 
660         // Update the Tuple
661         AbstractPage<K, V> leaf = ( AbstractPage<K, V> ) ( parentPos.page );
662 
663         // The key
664         Tuple<K, V> tuple = new Tuple<K, V>();
665         tuple.setKey( leaf.getKey( parentPos.pos ) );
666 
667         // The value
668         ValueHolder<V> valueHolder = leaf.getValue( parentPos.pos );
669         parentPos.valueCursor = valueHolder.getCursor();
670         V value = parentPos.valueCursor.next();
671         tuple.setValue( value );
672 
673         return tuple;
674     }
675 
676 
677     /**
678      * Tells if the cursor can return a previous key
679      *
680      * @return true if there are some more keys
681      * @throws IOException
682      * @throws EndOfFileExceededException
683      */
684     public boolean hasPrevKey() throws EndOfFileExceededException, IOException
685     {
686         // First check that we have elements in the BTree
687         if ( ( stack == null ) || ( stack.length == 0 ) )
688         {
689             // This is the end : no more key
690             return false;
691         }
692 
693         ParentPos<K, V> parentPos = stack[depth];
694 
695         if ( parentPos.page == null )
696         {
697             // This is the end : no more key
698             return false;
699         }
700 
701         switch ( parentPos.pos )
702         {
703             case 0:
704                 // Beginning of the leaf. We have to go back into the stack up to the
705                 // parent, and down to the leaf
706                 return hasPrevParentPos();
707 
708             case -1:
709                 // no previous key
710                 return false;
711 
712             default:
713                 // we have a previous key
714                 return true;
715         }
716     }
717 
718 
719     /**
720      * Tells if there is a next ParentPos
721      *
722      * @return the new ParentPos instance, or null if we have no following leaf
723      * @throws IOException
724      * @throws EndOfFileExceededException
725      */
726     private boolean hasNextParentPos() throws EndOfFileExceededException, IOException
727     {
728         if ( depth == 0 )
729         {
730             // No need to go any further, there is only one leaf in the btree
731             return false;
732         }
733 
734         int currentDepth = depth - 1;
735         Page<K, V> child = null;
736 
737         // First, go up the tree until we find a Node which has some element on the right
738         while ( currentDepth >= 0 )
739         {
740             // We first go up the tree, until we reach a page whose current position
741             // is not the last one
742             ParentPos<K, V> parentPos = stack[currentDepth];
743 
744             if ( parentPos.pos + 1 > parentPos.page.getNbElems() )
745             {
746                 // No more element on the right : go up
747                 currentDepth--;
748             }
749             else
750             {
751                 // We can pick the next element at this level
752                 child = ( ( AbstractPage<K, V> ) parentPos.page ).getPage( parentPos.pos + 1 );
753 
754                 // and go down the tree through the nodes
755                 while ( currentDepth < depth - 1 )
756                 {
757                     currentDepth++;
758                     child = ( ( AbstractPage<K, V> ) child ).getPage( 0 );
759                 }
760 
761                 return true;
762             }
763         }
764 
765         return false;
766     }
767 
768 
769     /**
770      * Find the leaf containing the following elements.
771      *
772      * @return the new ParentPos instance, or null if we have no following leaf
773      * @throws IOException
774      * @throws EndOfFileExceededException
775      */
776     private ParentPos<K, V> findNextParentPos() throws EndOfFileExceededException, IOException
777     {
778         if ( depth == 0 )
779         {
780             // No need to go any further, there is only one leaf in the btree
781             return null;
782         }
783 
784         int currentDepth = depth - 1;
785         Page<K, V> child = null;
786 
787         // First, go up the tree until we find a Node which has some element on the right
788         while ( currentDepth >= 0 )
789         {
790             // We first go up the tree, until we reach a page whose current position
791             // is not the last one
792             ParentPos<K, V> parentPos = stack[currentDepth];
793 
794             if ( parentPos.pos + 1 > parentPos.page.getNbElems() )
795             {
796                 // No more element on the right : go up
797                 currentDepth--;
798             }
799             else
800             {
801                 // We can pick the next element at this level
802                 parentPos.pos++;
803                 child = ( ( AbstractPage<K, V> ) parentPos.page ).getPage( parentPos.pos );
804 
805                 // and go down the tree through the nodes
806                 while ( currentDepth < depth - 1 )
807                 {
808                     currentDepth++;
809                     parentPos = stack[currentDepth];
810                     parentPos.pos = 0;
811                     parentPos.page = child;
812                     child = ( ( AbstractPage<K, V> ) child ).getPage( 0 );
813                 }
814 
815                 // and the leaf
816                 parentPos = stack[depth];
817                 parentPos.page = child;
818                 parentPos.pos = 0;
819                 parentPos.valueCursor = ( ( AbstractPage<K, V> ) child ).getValue( 0 ).getCursor();
820 
821                 return parentPos;
822             }
823         }
824 
825         return null;
826     }
827 
828 
829     /**
830      * Find the leaf containing the previous elements.
831      *
832      * @return the new ParentPos instance, or null if we have no previous leaf
833      * @throws IOException
834      * @throws EndOfFileExceededException
835      */
836     private ParentPos<K, V> findPrevParentPos() throws EndOfFileExceededException, IOException
837     {
838         if ( depth == 0 )
839         {
840             // No need to go any further, there is only one leaf in the btree
841             return null;
842         }
843 
844         int currentDepth = depth - 1;
845         Page<K, V> child = null;
846 
847         // First, go up the tree until we find a Node which has some element on the left
848         while ( currentDepth >= 0 )
849         {
850             // We first go up the tree, until we reach a page whose current position
851             // is not the last one
852             ParentPos<K, V> parentPos = stack[currentDepth];
853 
854             if ( parentPos.pos == 0 )
855             {
856                 // No more element on the right : go up
857                 currentDepth--;
858             }
859             else
860             {
861                 // We can pick the next element at this level
862                 parentPos.pos--;
863                 child = ( ( AbstractPage<K, V> ) parentPos.page ).getPage( parentPos.pos );
864 
865                 // and go down the tree through the nodes
866                 while ( currentDepth < depth - 1 )
867                 {
868                     currentDepth++;
869                     parentPos = stack[currentDepth];
870                     parentPos.pos = child.getNbElems();
871                     parentPos.page = child;
872                     child = ( ( AbstractPage<K, V> ) parentPos.page ).getPage( parentPos.page.getNbElems() );
873                 }
874 
875                 // and the leaf
876                 parentPos = stack[depth];
877                 parentPos.pos = child.getNbElems() - 1;
878                 parentPos.page = child;
879                 ValueHolder<V> valueHolder = ( ( AbstractPage<K, V> ) parentPos.page ).getValue( parentPos.pos );
880                 parentPos.valueCursor = valueHolder.getCursor();
881                 parentPos.valueCursor.afterLast();
882 
883                 return parentPos;
884             }
885         }
886 
887         return null;
888     }
889 
890 
891     /**
892      * Tells if there is a prev ParentPos
893      *
894      * @return the new ParentPos instance, or null if we have no previous leaf
895      * @throws IOException
896      * @throws EndOfFileExceededException
897      */
898     private boolean hasPrevParentPos() throws EndOfFileExceededException, IOException
899     {
900         if ( depth == 0 )
901         {
902             // No need to go any further, there is only one leaf in the btree
903             return false;
904         }
905 
906         int currentDepth = depth - 1;
907         Page<K, V> child = null;
908 
909         // First, go up the tree until we find a Node which has some element on the right
910         while ( currentDepth >= 0 )
911         {
912             // We first go up the tree, until we reach a page whose current position
913             // is not the last one
914             ParentPos<K, V> parentPos = stack[currentDepth];
915 
916             if ( parentPos.pos == 0 )
917             {
918                 // No more element on the left : go up
919                 currentDepth--;
920             }
921             else
922             {
923                 // We can pick the previous element at this level
924                 child = ( ( AbstractPage<K, V> ) parentPos.page ).getPage( parentPos.pos - 1 );
925 
926                 // and go down the tree through the nodes
927                 while ( currentDepth < depth - 1 )
928                 {
929                     currentDepth++;
930                     child = ( ( AbstractPage<K, V> ) child ).getPage( child.getNbElems() );
931                 }
932 
933                 return true;
934             }
935         }
936 
937         return false;
938     }
939 
940 
941     /**
942      * {@inheritDoc}
943      */
944     public void close()
945     {
946         transaction.close();
947     }
948 
949 
950     /**
951      * Get the creation date
952      * @return The creation date for this cursor
953      */
954     public long getCreationDate()
955     {
956         return transaction.getCreationDate();
957     }
958 
959 
960     /**
961      * Get the current revision
962      *
963      * @return The revision this cursor is based on
964      */
965     public long getRevision()
966     {
967         return transaction.getRevision();
968     }
969 
970 
971     public String toString()
972     {
973         StringBuilder sb = new StringBuilder();
974 
975         sb.append( "TupleCursor, depth = " ).append( depth ).append( "\n" );
976 
977         for ( int i = 0; i <= depth; i++ )
978         {
979             sb.append( "    " ).append( stack[i] ).append( "\n" );
980         }
981 
982         return sb.toString();
983     }
984 }