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