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