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.managed;
21  
22  
23  import java.io.IOException;
24  import java.lang.reflect.Array;
25  import java.util.LinkedList;
26  import java.util.List;
27  
28  import org.apache.directory.mavibot.btree.Tuple;
29  import org.apache.directory.mavibot.btree.ValueCursor;
30  import org.apache.directory.mavibot.btree.exception.EndOfFileExceededException;
31  import org.apache.directory.mavibot.btree.exception.KeyNotFoundException;
32  
33  
34  /**
35   * A MVCC Node. It stores the keys and references to its children page. It does not
36   * contain any value.
37   * 
38   * @param <K> The type for the Key
39   * @param <V> The type for the stored value
40   *
41   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
42   */
43  /* No qualifier */class Node<K, V> extends AbstractPage<K, V>
44  {
45      /** Children pages associated with keys. */
46      protected ElementHolder<Page<K, V>, K, V>[] children;
47  
48  
49      /**
50       * Creates a new Node which will contain only one key, with references to
51       * a left and right page. This is a specific constructor used by the btree
52       * when the root was full when we added a new value.
53       * 
54       * @param btree the parent BTree
55       * @param revision the Node revision
56       * @param nbElems The number of elements in this Node
57       */
58      @SuppressWarnings("unchecked")
59      /* No qualifier */Node( BTree<K, V> btree, long revision, int nbElems )
60      {
61          super( btree, revision, nbElems );
62  
63          // Create the children array
64          children = ( ElementHolder<Page<K, V>, K, V>[] ) Array.newInstance( ElementHolder.class, nbElems + 1 );
65      }
66  
67  
68      /**
69       * Creates a new Node which will contain only one key, with references to
70       * a left and right page. This is a specific constructor used by the btree
71       * when the root was full when we added a new value.
72       * 
73       * @param btree the parent BTree
74       * @param revision the Node revision
75       * @param key The new key
76       * @param leftPage The left page
77       * @param rightPage The right page
78       */
79      @SuppressWarnings("unchecked")
80      /* No qualifier */Node( BTree<K, V> btree, long revision, K key, Page<K, V> leftPage, Page<K, V> rightPage )
81      {
82          super( btree, revision, 1 );
83  
84          // Create the children array, and store the left and right children
85          children = ( PageHolder<K, V>[] ) Array.newInstance( PageHolder.class,
86              btree.getPageSize() + 1 );
87  
88          children[0] = btree.createPageHolder( leftPage );
89          children[1] = btree.createPageHolder( rightPage );
90  
91          // Create the keys array and store the pivot into it
92          // We get the type of array to create from the btree
93          // Yes, this is an hack...
94          keys = ( KeyHolder[] ) Array.newInstance( KeyHolder.class, btree.getPageSize() );
95  
96          keys[0] = new KeyHolder<K>( btree.getKeySerializer(), key );
97      }
98  
99  
100     /**
101      * Creates a new Node which will contain only one key, with references to
102      * a left and right page. This is a specific constructor used by the btree
103      * when the root was full when we added a new value.
104      * 
105      * @param btree the parent BTree
106      * @param revision the Node revision
107      * @param key The new key
108      * @param leftPage The left page
109      * @param rightPage The right page
110      */
111     @SuppressWarnings("unchecked")
112     /* No qualifier */Node( BTree<K, V> btree, long revision, K key, ElementHolder<Page<K, V>, K, V> leftPage,
113         ElementHolder<Page<K, V>, K, V> rightPage )
114     {
115         super( btree, revision, 1 );
116 
117         // Create the children array, and store the left and right children
118         children = ( PageHolder<K, V>[] ) Array.newInstance( PageHolder.class,
119             btree.getPageSize() + 1 );
120 
121         children[0] = leftPage;
122         children[1] = rightPage;
123 
124         // Create the keys array and store the pivot into it
125         keys = ( KeyHolder[] ) Array.newInstance( KeyHolder.class, btree.getPageSize() );
126 
127         keys[0] = new KeyHolder<K>( btree.getKeySerializer(), key );
128     }
129 
130 
131     /**
132      * {@inheritDoc}
133      */
134     public InsertResult<K, V> insert( long revision, K key, V value ) throws IOException
135     {
136         // Find the key into this leaf
137         int pos = findPos( key );
138 
139         if ( pos < 0 )
140         {
141             // The key has been found in the page. As it's a Node, that means
142             // we must go down in the right child to insert the value
143             pos = -( pos++ );
144         }
145 
146         // Get the child page into which we will insert the <K, V> tuple
147         Page<K, V> child = children[pos].getValue( btree );
148 
149         // and insert the <K, V> into this child
150         InsertResult<K, V> result = child.insert( revision, key, value );
151 
152         // Ok, now, we have injected the <K, V> tuple down the tree. Let's check
153         // the result to see if we have to split the current page
154         if ( result instanceof ModifyResult )
155         {
156             // The child has been modified.
157             return replaceChild( revision, ( ModifyResult<K, V> ) result, pos );
158         }
159         else
160         {
161             // The child has been split. We have to insert the new pivot in the
162             // current page, and to reference the two new pages
163             SplitResult<K, V> splitResult = ( SplitResult<K, V> ) result;
164             K pivot = splitResult.getPivot();
165             Page<K, V> leftPage = splitResult.getLeftPage();
166             Page<K, V> rightPage = splitResult.getRightPage();
167 
168             // We have to deal with the two cases :
169             // - the current page is full, we have to split it
170             // - the current page is not full, we insert the new pivot
171             if ( nbElems == btree.getPageSize() )
172             {
173                 // The page is full
174                 result = addAndSplit( splitResult.getCopiedPages(), revision, pivot, leftPage, rightPage, pos );
175             }
176             else
177             {
178                 // The page can contain the new pivot, let's insert it
179                 result = insertChild( splitResult.getCopiedPages(), revision, pivot, leftPage, rightPage, pos );
180             }
181 
182             return result;
183         }
184     }
185 
186 
187     /**
188      * Modifies the current node after a remove has been done in one of its children.
189      * The node won't be merged with another node.
190      * 
191      * @param removeResult The result of a remove operation
192      * @param index the position of the key, not transformed
193      * @param pos The position of the key, as a positive value
194      * @param found If the key has been found in the page
195      * @return The new result
196      * @throws IOException If we have an error while trying to access the page
197      */
198     private RemoveResult<K, V> handleRemoveResult( RemoveResult<K, V> removeResult, int index, int pos, boolean found )
199         throws IOException
200     {
201         // Simplest case : the element has been removed from the underlying page,
202         // we just have to copy the current page an modify the reference to link to
203         // the modified page.
204         Node<K, V> newPage = copy( revision );
205 
206         Page<K, V> modifiedPage = removeResult.getModifiedPage();
207 
208         if ( found )
209         {
210             newPage.children[index + 1] = createHolder( modifiedPage );
211         }
212         else
213         {
214             newPage.children[index] = createHolder( modifiedPage );
215         }
216 
217         if ( pos < 0 )
218         {
219             newPage.keys[index].setKey( removeResult.getModifiedPage().getLeftMostKey() );
220         }
221 
222         // Modify the result and return
223         removeResult.setModifiedPage( newPage );
224         removeResult.addCopiedPage( this );
225 
226         return removeResult;
227     }
228 
229 
230     /**
231      * Handles the removal of an element from the root page, when two of its children
232      * have been merged.
233      * 
234      * @param mergedResult The merge result
235      * @param pos The position in the current root
236      * @param found Tells if the removed key is present in the root page
237      * @return The resulting root page
238      * @throws IOException If we have an error while trying to access the page
239      */
240     private RemoveResult<K, V> handleRootRemove( MergedWithSiblingResult<K, V> mergedResult, int pos, boolean found )
241         throws IOException
242     {
243         RemoveResult<K, V> removeResult = null;
244 
245         // If the current node contains only one key, then the merged result will be
246         // the new root. Deal with this case
247         if ( nbElems == 1 )
248         {
249             removeResult = new RemoveResult<K, V>( mergedResult.getCopiedPages(), mergedResult.getModifiedPage(),
250                 mergedResult.getRemovedElement() );
251 
252             removeResult.addCopiedPage( this );
253         }
254         else
255         {
256             // Remove the element and update the reference to the changed pages
257             removeResult = removeKey( mergedResult, revision, pos );
258         }
259 
260         return removeResult;
261     }
262 
263 
264     /**
265      * Borrows an element from the right sibling, creating a new sibling with one
266      * less element and creating a new page where the element to remove has been
267      * deleted and the borrowed element added on the right.
268      * 
269      * @param revision The new revision for all the pages
270      * @param sibling The right sibling
271      * @param pos The position of the element to remove
272      * @return The resulting pages
273      * @throws IOException If we have an error while trying to access the page
274      */
275     private DeleteResult<K, V> borrowFromRight( long revision, MergedWithSiblingResult<K, V> mergedResult,
276         Node<K, V> sibling, int pos ) throws IOException
277     {
278         // Create the new sibling, with one less element at the beginning
279         Node<K, V> newSibling = new Node<K, V>( btree, revision, sibling.getNbElems() - 1 );
280 
281         K siblingKey = sibling.children[0].getValue( btree ).getLeftMostKey();
282 
283         // Copy the keys and children of the old sibling in the new sibling
284         System.arraycopy( sibling.keys, 1, newSibling.keys, 0, newSibling.getNbElems() );
285         System.arraycopy( sibling.children, 1, newSibling.children, 0, newSibling.getNbElems() + 1 );
286 
287         // Create the new page and add the new element at the end
288         // First copy the current node, with the same size
289         Node<K, V> newNode = new Node<K, V>( btree, revision, nbElems );
290 
291         // Copy the keys and the values up to the insertion position
292         int index = Math.abs( pos );
293 
294         // Copy the key and children from sibling
295         newNode.keys[nbElems - 1] = new KeyHolder<K>( btree.getKeySerializer(), siblingKey ); // 1
296         newNode.children[nbElems] = sibling.children[0]; // 8
297 
298         if ( index < 2 )
299         {
300             // Copy the keys
301             System.arraycopy( keys, 1, newNode.keys, 0, nbElems - 1 );
302 
303             // Inject the modified page
304             Page<K, V> modifiedPage = mergedResult.getModifiedPage();
305             newNode.children[0] = createHolder( modifiedPage );
306 
307             // Copy the children
308             System.arraycopy( children, 2, newNode.children, 1, nbElems - 1 );
309         }
310         else
311         {
312             if ( index > 2 )
313             {
314                 // Copy the keys before the deletion point
315                 System.arraycopy( keys, 0, newNode.keys, 0, index - 2 ); // 4
316             }
317 
318             // Inject the new modified page key
319             newNode.keys[index - 2] = new KeyHolder<K>( btree.getKeySerializer(), mergedResult.getModifiedPage()
320                 .getLeftMostKey() ); // 2
321 
322             if ( index < nbElems )
323             {
324                 // Copy the remaining keys after the deletion point
325                 System.arraycopy( keys, index, newNode.keys, index - 1, nbElems - index ); // 3
326 
327                 // Copy the remaining children after the deletion point
328                 System.arraycopy( children, index + 1, newNode.children, index, nbElems - index ); // 7
329             }
330 
331             // Copy the children before the deletion point
332             System.arraycopy( children, 0, newNode.children, 0, index - 1 ); // 5
333 
334             // Inject the modified page
335             Page<K, V> modifiedPage = mergedResult.getModifiedPage();
336             newNode.children[index - 1] = createHolder( modifiedPage ); // 6
337         }
338 
339         // Create the result
340         DeleteResult<K, V> result = new BorrowedFromRightResult<K, V>( mergedResult.getCopiedPages(), newNode,
341             newSibling, mergedResult.getRemovedElement() );
342 
343         result.addCopiedPage( this );
344         result.addCopiedPage( sibling );
345 
346         return result;
347     }
348 
349 
350     /**
351      * Borrows an element from the left sibling, creating a new sibling with one
352      * less element and creating a new page where the element to remove has been
353      * deleted and the borrowed element added on the left.
354      * 
355      * @param revision The new revision for all the pages
356      * @param sibling The left sibling
357      * @param pos The position of the element to remove
358      * @return The resulting pages
359      * @throws IOException If we have an error while trying to access the page
360      */
361     private DeleteResult<K, V> borrowFromLeft( long revision, MergedWithSiblingResult<K, V> mergedResult,
362         Node<K, V> sibling, int pos ) throws IOException
363     {
364         // The sibling is on the left, borrow the rightmost element
365         Page<K, V> siblingChild = sibling.children[sibling.nbElems].getValue( btree );
366 
367         // Create the new sibling, with one less element at the end
368         Node<K, V> newSibling = new Node<K, V>( btree, revision, sibling.getNbElems() - 1 );
369 
370         // Copy the keys and children of the old sibling in the new sibling
371         System.arraycopy( sibling.keys, 0, newSibling.keys, 0, newSibling.getNbElems() );
372         System.arraycopy( sibling.children, 0, newSibling.children, 0, newSibling.getNbElems() + 1 );
373 
374         // Create the new page and add the new element at the beginning
375         // First copy the current node, with the same size
376         Node<K, V> newNode = new Node<K, V>( btree, revision, nbElems );
377 
378         // Sets the first children
379         newNode.children[0] = createHolder( siblingChild ); //1
380 
381         int index = Math.abs( pos );
382 
383         if ( index < 2 )
384         {
385             newNode.keys[0] = new KeyHolder<K>( btree.getKeySerializer(), mergedResult.getModifiedPage()
386                 .getLeftMostKey() );
387             System.arraycopy( keys, 1, newNode.keys, 1, nbElems - 1 );
388 
389             Page<K, V> modifiedPage = mergedResult.getModifiedPage();
390             newNode.children[1] = createHolder( modifiedPage );
391             System.arraycopy( children, 2, newNode.children, 2, nbElems - 1 );
392         }
393         else
394         {
395             // Set the first key
396             newNode.keys[0] = new KeyHolder<K>( btree.getKeySerializer(), children[0].getValue( btree )
397                 .getLeftMostKey() ); //2
398 
399             if ( index > 2 )
400             {
401                 // Copy the keys before the deletion point
402                 System.arraycopy( keys, 0, newNode.keys, 1, index - 2 ); // 4
403             }
404 
405             // Inject the modified key
406             newNode.keys[index - 1] = new KeyHolder<K>( btree.getKeySerializer(), mergedResult.getModifiedPage()
407                 .getLeftMostKey() ); // 3
408 
409             if ( index < nbElems )
410             {
411                 // Add copy the remaining keys after the deletion point
412                 System.arraycopy( keys, index, newNode.keys, index, nbElems - index ); // 5
413 
414                 // Copy the remaining children after the insertion point
415                 System.arraycopy( children, index + 1, newNode.children, index + 1, nbElems - index ); // 8
416             }
417 
418             // Copy the children before the insertion point
419             System.arraycopy( children, 0, newNode.children, 1, index - 1 ); // 6
420 
421             // Insert the modified page
422             Page<K, V> modifiedPage = mergedResult.getModifiedPage();
423             newNode.children[index] = createHolder( modifiedPage ); // 7
424         }
425 
426         // Create the result
427         DeleteResult<K, V> result = new BorrowedFromLeftResult<K, V>( mergedResult.getCopiedPages(), newNode,
428             newSibling,
429             mergedResult.getRemovedElement() );
430 
431         result.addCopiedPage( this );
432         result.addCopiedPage( sibling );
433 
434         return result;
435     }
436 
437 
438     /**
439      * We have to merge the node with its sibling, both have N/2 elements before the element
440      * removal.
441      * 
442      * @param revision The revision
443      * @param mergedResult The result of the merge
444      * @param sibling The Page we will merge the current page with
445      * @param isLeft Tells if the sibling is on the left
446      * @param pos The position of the key that has been removed
447      * @return The page resulting of the merge
448      * @throws IOException If we have an error while trying to access the page
449      */
450     private DeleteResult<K, V> mergeWithSibling( long revision, MergedWithSiblingResult<K, V> mergedResult,
451         Node<K, V> sibling, boolean isLeft, int pos ) throws IOException
452     {
453         // Create the new node. It will contain N - 1 elements (the maximum number)
454         // as we merge two nodes that contain N/2 elements minus the one we remove
455         Node<K, V> newNode = new Node<K, V>( btree, revision, btree.getPageSize() );
456         Tuple<K, V> removedElement = mergedResult.getRemovedElement();
457         int half = btree.getPageSize() / 2;
458         int index = Math.abs( pos );
459 
460         if ( isLeft )
461         {
462             // The sibling is on the left. Copy all of its elements in the new node first
463             System.arraycopy( sibling.keys, 0, newNode.keys, 0, half ); //1
464             System.arraycopy( sibling.children, 0, newNode.children, 0, half + 1 ); //2
465 
466             // Then copy all the elements up to the deletion point
467             if ( index < 2 )
468             {
469                 newNode.keys[half] = new KeyHolder<K>( btree.getKeySerializer(), mergedResult.getModifiedPage()
470                     .getLeftMostKey() );
471                 System.arraycopy( keys, 1, newNode.keys, half + 1, half - 1 );
472 
473                 Page<K, V> modifiedPage = mergedResult.getModifiedPage();
474                 newNode.children[half + 1] = createHolder( modifiedPage );
475                 System.arraycopy( children, 2, newNode.children, half + 2, half - 1 );
476             }
477             else
478             {
479                 // Copy the left part of the node keys up to the deletion point
480                 // Insert the new key
481                 newNode.keys[half] = new KeyHolder<K>( btree.getKeySerializer(), children[0].getValue( btree )
482                     .getLeftMostKey() ); // 3
483 
484                 if ( index > 2 )
485                 {
486                     System.arraycopy( keys, 0, newNode.keys, half + 1, index - 2 ); //4
487                 }
488 
489                 // Inject the new merged key
490                 newNode.keys[half + index - 1] = new KeyHolder<K>( btree.getKeySerializer(), mergedResult
491                     .getModifiedPage().getLeftMostKey() ); //5
492 
493                 if ( index < half )
494                 {
495                     System.arraycopy( keys, index, newNode.keys, half + index, half - index ); //6
496                     System.arraycopy( children, index + 1, newNode.children, half + index + 1, half - index ); //9
497                 }
498 
499                 // Copy the children before the deletion point
500                 System.arraycopy( children, 0, newNode.children, half + 1, index - 1 ); // 7
501 
502                 // Inject the new merged child
503                 Page<K, V> modifiedPage = mergedResult.getModifiedPage();
504                 newNode.children[half + index] = createHolder( modifiedPage ); //8
505             }
506         }
507         else
508         {
509             // The sibling is on the right.
510             if ( index < 2 )
511             {
512                 // Copy the keys
513                 System.arraycopy( keys, 1, newNode.keys, 0, half - 1 );
514 
515                 // Insert the first child
516                 Page<K, V> modifiedPage = mergedResult.getModifiedPage();
517                 newNode.children[0] = createHolder( modifiedPage );
518 
519                 // Copy the node children
520                 System.arraycopy( children, 2, newNode.children, 1, half - 1 );
521             }
522             else
523             {
524                 // Copy the keys and children before the deletion point
525                 if ( index > 2 )
526                 {
527                     // Copy the first keys
528                     System.arraycopy( keys, 0, newNode.keys, 0, index - 2 ); //1
529                 }
530 
531                 // Copy the first children
532                 System.arraycopy( children, 0, newNode.children, 0, index - 1 ); //6
533 
534                 // Inject the modified key
535                 newNode.keys[index - 2] = new KeyHolder<K>( btree.getKeySerializer(), mergedResult.getModifiedPage()
536                     .getLeftMostKey() ); //2
537 
538                 // Inject the modified children
539                 Page<K, V> modifiedPage = mergedResult.getModifiedPage();
540                 newNode.children[index - 1] = createHolder( modifiedPage ); // 7
541 
542                 // Add the remaining node's key if needed
543                 if ( index < half )
544                 {
545                     System.arraycopy( keys, index, newNode.keys, index - 1, half - index ); //5
546 
547                     // Add the remining children if below half
548                     System.arraycopy( children, index + 1, newNode.children, index, half - index ); // 8
549                 }
550             }
551 
552             // Inject the new key from sibling
553             newNode.keys[half - 1] = new KeyHolder<K>( btree.getKeySerializer(), sibling.findLeftMost().getKey() ); //3
554 
555             // Copy the sibling keys
556             System.arraycopy( sibling.keys, 0, newNode.keys, half, half );
557 
558             // Add the sibling children
559             System.arraycopy( sibling.children, 0, newNode.children, half, half + 1 ); // 9
560         }
561 
562         // And create the result
563         DeleteResult<K, V> result = new MergedWithSiblingResult<K, V>( mergedResult.getCopiedPages(), newNode,
564             removedElement );
565 
566         result.addCopiedPage( this );
567         result.addCopiedPage( sibling );
568 
569         return result;
570     }
571 
572 
573     /**
574      * {@inheritDoc}
575      */
576     public DeleteResult<K, V> delete( long revision, K key, V value, Page<K, V> parent, int parentPos )
577         throws IOException
578     {
579         // We first try to delete the element from the child it belongs to
580         // Find the key in the page
581         int pos = findPos( key );
582         boolean found = pos < 0;
583         int index = pos;
584         Page<K, V> child = null;
585         DeleteResult<K, V> deleteResult = null;
586 
587         if ( found )
588         {
589             index = -( pos + 1 );
590             child = children[-pos].getValue( btree );
591             deleteResult = child.delete( revision, key, value, this, -pos );
592         }
593         else
594         {
595             child = children[pos].getValue( btree );
596             deleteResult = child.delete( revision, key, value, this, pos );
597         }
598 
599         // If the key is not present in the tree, we simply return
600         if ( deleteResult instanceof NotPresentResult )
601         {
602             // Nothing to do...
603             return deleteResult;
604         }
605 
606         // If we just modified the child, return a modified page
607         if ( deleteResult instanceof RemoveResult )
608         {
609             RemoveResult<K, V> removeResult = handleRemoveResult( ( RemoveResult<K, V> ) deleteResult, index, pos,
610                 found );
611 
612             return removeResult;
613         }
614 
615         // If we had to borrow an element in the child, then have to update
616         // the current page
617         if ( deleteResult instanceof BorrowedFromSiblingResult )
618         {
619             RemoveResult<K, V> removeResult = handleBorrowedResult( ( BorrowedFromSiblingResult<K, V> ) deleteResult,
620                 pos );
621 
622             return removeResult;
623         }
624 
625         // Last, not least, we have merged two child pages. We now have to remove
626         // an element from the local page, and to deal with the result.
627         if ( deleteResult instanceof MergedWithSiblingResult )
628         {
629             MergedWithSiblingResult<K, V> mergedResult = ( MergedWithSiblingResult<K, V> ) deleteResult;
630 
631             // If the parent is null, then this page is the root page.
632             if ( parent == null )
633             {
634                 RemoveResult<K, V> result = handleRootRemove( mergedResult, pos, found );
635 
636                 return result;
637             }
638 
639             // We have some parent. Check if the current page is not half full
640             int halfSize = btree.getPageSize() / 2;
641 
642             if ( nbElems > halfSize )
643             {
644                 // The page has more than N/2 elements.
645                 // We simply remove the element from the page, and if it was the leftmost,
646                 // we return the new pivot (it will replace any instance of the removed
647                 // key in its parents)
648                 RemoveResult<K, V> result = removeKey( mergedResult, revision, pos );
649 
650                 return result;
651             }
652             else
653             {
654                 // We will remove one element from a page that will have less than N/2 elements,
655                 // which will lead to some reorganization : either we can borrow an element from
656                 // a sibling, or we will have to merge two pages
657                 int siblingPos = selectSibling( ( Node<K, V> ) parent, parentPos );
658 
659                 Node<K, V> sibling = ( Node<K, V> ) ( ( ( Node<K, V> ) parent ).children[siblingPos].getValue( btree ) );
660 
661                 if ( sibling.getNbElems() > halfSize )
662                 {
663                     // The sibling contains enough elements
664                     // We can borrow the element from the sibling
665                     if ( siblingPos < parentPos )
666                     {
667                         DeleteResult<K, V> result = borrowFromLeft( revision, mergedResult, sibling, pos );
668 
669                         return result;
670                     }
671                     else
672                     {
673                         // Borrow from the right
674                         DeleteResult<K, V> result = borrowFromRight( revision, mergedResult, sibling, pos );
675 
676                         return result;
677                     }
678                 }
679                 else
680                 {
681                     // We need to merge the sibling with the current page
682                     DeleteResult<K, V> result = mergeWithSibling( revision, mergedResult, sibling,
683                         ( siblingPos < parentPos ), pos );
684 
685                     return result;
686                 }
687             }
688         }
689 
690         // We should never reach this point
691         return null;
692     }
693 
694 
695     /**
696      * The deletion in a children has moved an element from one of its sibling. The key
697      * is present in the current node.
698      * @param borrowedResult The result of the deletion from the children
699      * @param pos The position the key was found in the current node
700      * @return The result
701      * @throws IOException If we have an error while trying to access the page
702      */
703     private RemoveResult<K, V> handleBorrowedResult( BorrowedFromSiblingResult<K, V> borrowedResult, int pos )
704         throws IOException
705     {
706         Page<K, V> modifiedPage = borrowedResult.getModifiedPage();
707         Page<K, V> modifiedSibling = borrowedResult.getModifiedSibling();
708 
709         Node<K, V> newPage = copy( revision );
710 
711         if ( pos < 0 )
712         {
713             pos = -( pos + 1 );
714 
715             if ( borrowedResult.isFromRight() )
716             {
717                 // Update the keys
718                 newPage.keys[pos] = new KeyHolder<K>( btree.getKeySerializer(), modifiedPage.findLeftMost().getKey() );
719                 newPage.keys[pos + 1] = new KeyHolder<K>( btree.getKeySerializer(), modifiedSibling.findLeftMost()
720                     .getKey() );
721 
722                 // Update the children
723                 newPage.children[pos + 1] = createHolder( modifiedPage );
724                 newPage.children[pos + 2] = createHolder( modifiedSibling );
725             }
726             else
727             {
728                 // Update the keys
729                 newPage.keys[pos] = new KeyHolder<K>( btree.getKeySerializer(), modifiedPage.findLeftMost().getKey() );
730 
731                 // Update the children
732                 newPage.children[pos] = createHolder( modifiedSibling );
733                 newPage.children[pos + 1] = createHolder( modifiedPage );
734             }
735         }
736         else
737         {
738             if ( borrowedResult.isFromRight() )
739             {
740                 // Update the keys
741                 newPage.keys[pos] = new KeyHolder<K>( btree.getKeySerializer(), modifiedSibling.findLeftMost().getKey() );
742 
743                 // Update the children
744                 newPage.children[pos] = createHolder( modifiedPage );
745                 newPage.children[pos + 1] = createHolder( modifiedSibling );
746             }
747             else
748             {
749                 // Update the keys
750                 newPage.keys[pos - 1] = new KeyHolder<K>( btree.getKeySerializer(), modifiedPage.findLeftMost()
751                     .getKey() );
752 
753                 // Update the children
754                 newPage.children[pos - 1] = createHolder( modifiedSibling );
755                 newPage.children[pos] = createHolder( modifiedPage );
756             }
757         }
758 
759         // Modify the result and return
760         RemoveResult<K, V> removeResult = new RemoveResult<K, V>( borrowedResult.getCopiedPages(), newPage,
761             borrowedResult.getRemovedElement() );
762 
763         removeResult.addCopiedPage( this );
764 
765         return removeResult;
766     }
767 
768 
769     /**
770      * Remove the key at a given position.
771      * 
772      * @param mergedResult The page we will remove a key from
773      * @param revision The revision of the modified page
774      * @param pos The position into the page of the element to remove
775      * @return The modified page with the <K,V> element added
776      * @throws IOException If we have an error while trying to access the page
777      */
778     private RemoveResult<K, V> removeKey( MergedWithSiblingResult<K, V> mergedResult, long revision, int pos )
779         throws IOException
780     {
781         // First copy the current page, but remove one element in the copied page
782         Node<K, V> newNode = new Node<K, V>( btree, revision, nbElems - 1 );
783 
784         int index = Math.abs( pos ) - 2;
785 
786         //
787         if ( index < 0 )
788         {
789             // Copy the keys and the children
790             System.arraycopy( keys, 1, newNode.keys, 0, newNode.nbElems );
791             Page<K, V> modifiedPage = mergedResult.getModifiedPage();
792             newNode.children[0] = createHolder( modifiedPage );
793             System.arraycopy( children, 2, newNode.children, 1, nbElems - 1 );
794         }
795         else
796         {
797             // Copy the keys
798             if ( index > 0 )
799             {
800                 System.arraycopy( keys, 0, newNode.keys, 0, index );
801             }
802 
803             newNode.keys[index] = new KeyHolder<K>( btree.getKeySerializer(), mergedResult.getModifiedPage()
804                 .findLeftMost().getKey() );
805 
806             if ( index < nbElems - 2 )
807             {
808                 System.arraycopy( keys, index + 2, newNode.keys, index + 1, nbElems - index - 2 );
809             }
810 
811             // Copy the children
812             System.arraycopy( children, 0, newNode.children, 0, index + 1 );
813 
814             Page<K, V> modifiedPage = mergedResult.getModifiedPage();
815             newNode.children[index + 1] = createHolder( modifiedPage );
816 
817             if ( index < nbElems - 2 )
818             {
819                 System.arraycopy( children, index + 3, newNode.children, index + 2, nbElems - index - 2 );
820             }
821         }
822 
823         // Create the result
824         RemoveResult<K, V> result = new RemoveResult<K, V>( mergedResult.getCopiedPages(), newNode,
825             mergedResult.getRemovedElement() );
826 
827         result.addCopiedPage( this );
828 
829         return result;
830     }
831 
832 
833     /**
834      * {@inheritDoc}
835      */
836     public V get( K key ) throws IOException, KeyNotFoundException
837     {
838         int pos = findPos( key );
839 
840         if ( pos < 0 )
841         {
842             // Here, if we have found the key in the node, then we must go down into
843             // the right child, not the left one
844             return children[-pos].getValue( btree ).get( key );
845         }
846         else
847         {
848             return children[pos].getValue( btree ).get( key );
849         }
850     }
851 
852 
853     /**
854      * {@inheritDoc}
855      */
856     @Override
857     public ValueCursor<V> getValues( K key ) throws KeyNotFoundException, IOException, IllegalArgumentException
858     {
859         int pos = findPos( key );
860 
861         if ( pos < 0 )
862         {
863             // Here, if we have found the key in the node, then we must go down into
864             // the right child, not the left one
865             return children[-pos].getValue( btree ).getValues( key );
866         }
867         else
868         {
869             return children[pos].getValue( btree ).getValues( key );
870         }
871     }
872 
873 
874     /**
875      * {@inheritDoc}
876      */
877     @Override
878     public boolean hasKey( K key ) throws IOException
879     {
880         int pos = findPos( key );
881 
882         if ( pos < 0 )
883         {
884             // Here, if we have found the key in the node, then we must go down into
885             // the right child, not the left one
886             return children[-pos].getValue( btree ).hasKey( key );
887         }
888         else
889         {
890             Page<K, V> page = children[pos].getValue( btree );
891 
892             if ( page == null )
893             {
894                 System.out.println( "Page is null for pos = " + pos + ", children = " + children[pos] );
895             }
896 
897             return page.hasKey( key );
898         }
899     }
900 
901 
902     /**
903      * {@inheritDoc}
904      */
905     @Override
906     public boolean contains( K key, V value ) throws IOException
907     {
908         int pos = findPos( key );
909 
910         if ( pos < 0 )
911         {
912             // Here, if we have found the key in the node, then we must go down into
913             // the right child, not the left one
914             return children[-pos].getValue( btree ).contains( key, value );
915         }
916         else
917         {
918             return children[pos].getValue( btree ).contains( key, value );
919         }
920     }
921 
922 
923     /**
924      * Set the value at a give position
925      * 
926      * @param pos The position in the values array
927      * @param value the value to inject
928      */
929     public void setValue( int pos, ElementHolder<Page<K, V>, K, V> value )
930     {
931         children[pos] = value;
932     }
933 
934 
935     /**
936      * {@inheritDoc}
937      */
938     public Page<K, V> getReference( int pos ) throws IOException
939     {
940         if ( pos < nbElems + 1 )
941         {
942             return children[pos].getValue( btree );
943         }
944         else
945         {
946             return null;
947         }
948     }
949 
950 
951     /**
952      * {@inheritDoc}
953      */
954     public CursorImpl<K, V> browse( K key, Transaction<K, V> transaction, LinkedList<ParentPos<K, V>> stack )
955         throws IOException
956     {
957         int pos = findPos( key );
958 
959         if ( pos < 0 )
960         {
961             pos = -pos;
962         }
963 
964         // We first stack the current page
965         stack.push( new ParentPos<K, V>( this, pos ) );
966 
967         return children[pos].getValue( btree ).browse( key, transaction, stack );
968     }
969 
970 
971     /**
972      * {@inheritDoc}
973      */
974     public CursorImpl<K, V> browse( Transaction<K, V> transaction, LinkedList<ParentPos<K, V>> stack )
975         throws IOException
976     {
977         stack.push( new ParentPos<K, V>( this, 0 ) );
978 
979         return children[0].getValue( btree ).browse( transaction, stack );
980     }
981 
982 
983     /**
984      * This method is used when we have to replace a child in a page when we have
985      * found the key in the tree (the value will be changed, so we have made
986      * copies of the existing pages).
987      * 
988      * @param revision The current revision
989      * @param result The modified page
990      * @param pos The position of the found key
991      * @return A modified page
992      * @throws IOException If we have an error while trying to access the page
993      */
994     private InsertResult<K, V> replaceChild( long revision, ModifyResult<K, V> result, int pos ) throws IOException
995     {
996         // Just copy the current page and update its revision
997         Page<K, V> newPage = copy( revision );
998 
999         // Last, we update the children table of the newly created page
1000         // to point on the modified child
1001         Page<K, V> modifiedPage = result.getModifiedPage();
1002 
1003         ( ( Node<K, V> ) newPage ).children[pos] = createHolder( modifiedPage );
1004 
1005         // We can return the result, where we update the modifiedPage,
1006         // to avoid the creation of a new object
1007         result.modifiedPage = newPage;
1008 
1009         result.addCopiedPage( this );
1010 
1011         return result;
1012     }
1013 
1014 
1015     /**
1016      * Creates a new holder contaning a reference to a Page
1017      * 
1018      * @param page The page we will refer to
1019      * @return A holder contaning a reference to the child page
1020      * @throws IOException If we have an error while trying to access the page
1021      */
1022     private ElementHolder<Page<K, V>, K, V> createHolder( Page<K, V> page ) throws IOException
1023     {
1024         ElementHolder<Page<K, V>, K, V> holder = btree.getRecordManager().writePage( btree,
1025             page,
1026             revision );
1027 
1028         return holder;
1029     }
1030 
1031 
1032     /**
1033      * Adds a new key into a copy of the current page at a given position. We return the
1034      * modified page. The new page will have one more key than the current page.
1035      * 
1036      * @param copiedPages the list of copied pages
1037      * @param revision The revision of the modified page
1038      * @param key The key to insert
1039      * @param leftPage The left child
1040      * @param rightPage The right child
1041      * @param pos The position into the page
1042      * @return The modified page with the <K,V> element added
1043      * @throws IOException If we have an error while trying to access the page
1044      */
1045     private InsertResult<K, V> insertChild( List<Page<K, V>> copiedPages, long revision, K key, Page<K, V> leftPage,
1046         Page<K, V> rightPage, int pos )
1047         throws IOException
1048     {
1049         // First copy the current page, but add one element in the copied page
1050         Node<K, V> newNode = new Node<K, V>( btree, revision, nbElems + 1 );
1051 
1052         // Copy the keys and the children up to the insertion position
1053         if ( nbElems > 0 )
1054         {
1055             System.arraycopy( keys, 0, newNode.keys, 0, pos );
1056             System.arraycopy( children, 0, newNode.children, 0, pos );
1057         }
1058 
1059         // Add the new key and children
1060         newNode.keys[pos] = new KeyHolder<K>( btree.getKeySerializer(), key );
1061 
1062         // If the BTree is managed, we now have to write the modified page on disk
1063         // and to add this page to the list of modified pages
1064         newNode.children[pos] = createHolder( leftPage );
1065         newNode.children[pos + 1] = createHolder( rightPage );
1066 
1067         // And copy the remaining keys and children
1068         if ( nbElems > 0 )
1069         {
1070             System.arraycopy( keys, pos, newNode.keys, pos + 1, keys.length - pos );
1071             System.arraycopy( children, pos + 1, newNode.children, pos + 2, children.length - pos - 1 );
1072         }
1073 
1074         // Create the result
1075         InsertResult<K, V> result = new ModifyResult<K, V>( copiedPages, newNode, null );
1076         result.addCopiedPage( this );
1077 
1078         return result;
1079     }
1080 
1081 
1082     /**
1083      * Splits a full page into two new pages, a left, a right and a pivot element. The new pages will
1084      * each contains half of the original elements. <br/>
1085      * The pivot will be computed, depending on the place
1086      * we will inject the newly added element. <br/>
1087      * If the newly added element is in the middle, we will use it
1088      * as a pivot. Otherwise, we will use either the last element in the left page if the element is added
1089      * on the left, or the first element in the right page if it's added on the right.
1090      * 
1091      * @param copiedPages the list of copied pages
1092      * @param revision The new revision for all the created pages
1093      * @param pivot The key that will be move up after the split
1094      * @param leftPage The left child
1095      * @param rightPage The right child
1096      * @param pos The position of the insertion of the new element
1097      * @return An OverflowPage containing the pivot, and the new left and right pages
1098      * @throws IOException If we have an error while trying to access the page
1099      */
1100     private InsertResult<K, V> addAndSplit( List<Page<K, V>> copiedPages, long revision, K pivot, Page<K, V> leftPage,
1101         Page<K, V> rightPage, int pos ) throws IOException
1102     {
1103         int middle = btree.getPageSize() >> 1;
1104 
1105         // Create two new pages
1106         Node<K, V> newLeftPage = new Node<K, V>( btree, revision, middle );
1107         Node<K, V> newRightPage = new Node<K, V>( btree, revision, middle );
1108 
1109         // Determinate where to store the new value
1110         // If it's before the middle, insert the value on the left,
1111         // the key in the middle will become the new pivot
1112         if ( pos < middle )
1113         {
1114             // Copy the keys and the children up to the insertion position
1115             System.arraycopy( keys, 0, newLeftPage.keys, 0, pos );
1116             System.arraycopy( children, 0, newLeftPage.children, 0, pos );
1117 
1118             // Add the new element
1119             newLeftPage.keys[pos] = new KeyHolder<K>( btree.getKeySerializer(), pivot );
1120             newLeftPage.children[pos] = createHolder( leftPage );
1121             newLeftPage.children[pos + 1] = createHolder( rightPage );
1122 
1123             // And copy the remaining elements minus the new pivot
1124             System.arraycopy( keys, pos, newLeftPage.keys, pos + 1, middle - pos - 1 );
1125             System.arraycopy( children, pos + 1, newLeftPage.children, pos + 2, middle - pos - 1 );
1126 
1127             // Copy the keys and the children in the right page
1128             System.arraycopy( keys, middle, newRightPage.keys, 0, middle );
1129             System.arraycopy( children, middle, newRightPage.children, 0, middle + 1 );
1130 
1131             // Create the result
1132             InsertResult<K, V> result = new SplitResult<K, V>( copiedPages, keys[middle - 1].getKey(), newLeftPage,
1133                 newRightPage );
1134             result.addCopiedPage( this );
1135 
1136             return result;
1137         }
1138         else if ( pos == middle )
1139         {
1140             // A special case : the pivot will be propagated up in the tree
1141             // The left and right pages will be spread on the two new pages
1142             // Copy the keys and the children up to the insertion position (here, middle)
1143             System.arraycopy( keys, 0, newLeftPage.keys, 0, middle );
1144             System.arraycopy( children, 0, newLeftPage.children, 0, middle );
1145             newLeftPage.children[middle] = createHolder( leftPage );
1146 
1147             // And process the right page now
1148             System.arraycopy( keys, middle, newRightPage.keys, 0, middle );
1149             System.arraycopy( children, middle + 1, newRightPage.children, 1, middle );
1150             newRightPage.children[0] = createHolder( rightPage );
1151 
1152             // Create the result
1153             InsertResult<K, V> result = new SplitResult<K, V>( copiedPages, pivot, newLeftPage, newRightPage );
1154             result.addCopiedPage( this );
1155 
1156             return result;
1157         }
1158         else
1159         {
1160             // Copy the keys and the children up to the middle
1161             System.arraycopy( keys, 0, newLeftPage.keys, 0, middle );
1162             System.arraycopy( children, 0, newLeftPage.children, 0, middle + 1 );
1163 
1164             // Copy the keys and the children in the right page up to the pos
1165             System.arraycopy( keys, middle + 1, newRightPage.keys, 0, pos - middle - 1 );
1166             System.arraycopy( children, middle + 1, newRightPage.children, 0, pos - middle - 1 );
1167 
1168             // Add the new element
1169             newRightPage.keys[pos - middle - 1] = new KeyHolder<K>( btree.getKeySerializer(), pivot );
1170             newRightPage.children[pos - middle - 1] = createHolder( leftPage );
1171             newRightPage.children[pos - middle] = createHolder( rightPage );
1172 
1173             // And copy the remaining elements minus the new pivot
1174             System.arraycopy( keys, pos, newRightPage.keys, pos - middle, nbElems - pos );
1175             System.arraycopy( children, pos + 1, newRightPage.children, pos + 1 - middle, nbElems - pos );
1176 
1177             // Create the result
1178             InsertResult<K, V> result = new SplitResult<K, V>( copiedPages, keys[middle].getKey(), newLeftPage,
1179                 newRightPage );
1180             result.addCopiedPage( this );
1181 
1182             return result;
1183         }
1184     }
1185 
1186 
1187     /**
1188      * Copies the current page and all its keys, with a new revision.
1189      * 
1190      * @param revision The new revision
1191      * @return The copied page
1192      */
1193     protected Node<K, V> copy( long revision )
1194     {
1195         Node<K, V> newPage = new Node<K, V>( btree, revision, nbElems );
1196 
1197         // Copy the keys
1198         System.arraycopy( keys, 0, newPage.keys, 0, nbElems );
1199 
1200         // Copy the children
1201         System.arraycopy( children, 0, newPage.children, 0, nbElems + 1 );
1202 
1203         return newPage;
1204     }
1205 
1206 
1207     /**
1208      * {@inheritDoc}
1209      */
1210     public K getLeftMostKey() throws EndOfFileExceededException, IOException
1211     {
1212         return children[0].getValue( btree ).getLeftMostKey();
1213     }
1214 
1215 
1216     /**
1217      * {@inheritDoc}
1218      */
1219     public K getRightMostKey() throws EndOfFileExceededException, IOException
1220     {
1221         int index = ( nbElems + 1 ) - 1;
1222 
1223         if ( children[index] != null )
1224         {
1225             return children[index].getValue( btree ).getRightMostKey();
1226         }
1227 
1228         return children[nbElems - 1].getValue( btree ).getRightMostKey();
1229     }
1230 
1231 
1232     /**
1233      * {@inheritDoc}
1234      */
1235     public Tuple<K, V> findLeftMost() throws EndOfFileExceededException, IOException
1236     {
1237         return children[0].getValue( btree ).findLeftMost();
1238     }
1239 
1240 
1241     /**
1242      * {@inheritDoc}
1243      */
1244     public Tuple<K, V> findRightMost() throws EndOfFileExceededException, IOException
1245     {
1246         return children[nbElems].getValue( btree ).findRightMost();
1247     }
1248 
1249 
1250     /**
1251      * @see Object#toString()
1252      */
1253     public String toString()
1254     {
1255         StringBuilder sb = new StringBuilder();
1256 
1257         sb.append( "Node[" );
1258         sb.append( super.toString() );
1259         sb.append( "] -> {" );
1260 
1261         try
1262         {
1263             if ( nbElems > 0 )
1264             {
1265                 // Start with the first child
1266                 if ( children[0] == null )
1267                 {
1268                     sb.append( "null" );
1269                 }
1270                 else
1271                 {
1272                     sb.append( 'r' ).append( children[0].getValue( btree ).getRevision() );
1273                 }
1274 
1275                 for ( int i = 0; i < nbElems; i++ )
1276                 {
1277                     sb.append( "|<" ).append( keys[i] ).append( ">|" );
1278 
1279                     if ( children[i + 1] == null )
1280                     {
1281                         sb.append( "null" );
1282                     }
1283                     else
1284                     {
1285                         sb.append( 'r' ).append( children[i + 1].getValue( btree ).getRevision() );
1286                     }
1287                 }
1288             }
1289         }
1290         catch ( IOException ioe )
1291         {
1292             // Do nothing
1293         }
1294 
1295         sb.append( "}" );
1296 
1297         return sb.toString();
1298     }
1299 
1300 
1301     /**
1302      * {@inheritDoc}
1303      */
1304     public String dumpPage( String tabs )
1305     {
1306         StringBuilder sb = new StringBuilder();
1307 
1308         if ( nbElems > 0 )
1309         {
1310             try
1311             {
1312                 // Start with the first child
1313                 sb.append( children[0].getValue( btree ).dumpPage( tabs + "    " ) );
1314 
1315                 for ( int i = 0; i < nbElems; i++ )
1316                 {
1317                     sb.append( tabs );
1318                     sb.append( "<" );
1319                     sb.append( keys[i] ).append( ">\n" );
1320                     sb.append( children[i + 1].getValue( btree ).dumpPage( tabs + "    " ) );
1321                 }
1322             }
1323             catch ( IOException ioe )
1324             {
1325                 // Do nothing
1326             }
1327         }
1328 
1329         return sb.toString();
1330     }
1331 }