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