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