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 static org.apache.directory.mavibot.btree.InternalUtil.setDupsContainer;
24  
25  import java.io.IOException;
26  import java.lang.reflect.Array;
27  import java.util.LinkedList;
28  
29  import org.apache.directory.mavibot.btree.exception.EndOfFileExceededException;
30  import org.apache.directory.mavibot.btree.exception.KeyNotFoundException;
31  
32  
33  /**
34   * A MVCC Leaf. It stores the keys and values. It does not have any children.
35   * 
36   * @param <K> The type for the Key
37   * @param <V> The type for the stored value
38   *
39   * @author <a href="mailto:labs@labs.apache.org">Mavibot labs Project</a>
40   */
41  /* No qualifier */class Leaf<K, V> extends AbstractPage<K, V>
42  {
43      /** Values associated with keys */
44      protected ElementHolder<V, K, V>[] values;
45  
46  
47      /**
48       * Constructor used to create a new Leaf when we read it from a file.
49       * 
50       * @param btree The BTree this page belongs to.
51       */
52      /* No qualifier */Leaf( BTree<K, V> btree )
53      {
54          super( btree );
55      }
56  
57  
58      /**
59       * Internal constructor used to create Page instance used when a page is being copied or overflow
60       * 
61       * @param btree The BTree this page belongs to.
62       * @param revision The page revision
63       * @param nbElems The number of elements this page will contain
64       */
65      @SuppressWarnings("unchecked")
66      // Cannot create an array of generic objects
67      /* No qualifier */Leaf( BTree<K, V> btree, long revision, int nbElems )
68      {
69          super( btree, revision, nbElems );
70  
71          if ( btree.isAllowDuplicates() )
72          {
73              this.values = ( DuplicateKeyMemoryHolder<K, V>[] ) Array.newInstance( DuplicateKeyMemoryHolder.class,
74                  nbElems );
75          }
76          else
77          {
78              this.values = ( MemoryHolder<K, V>[] ) Array.newInstance( MemoryHolder.class, nbElems );
79          }
80      }
81  
82  
83      /**
84       * {@inheritDoc}
85       */
86      public InsertResult<K, V> insert( long revision, K key, V value ) throws IOException
87      {
88          // Find the key into this leaf
89          int pos = findPos( key );
90  
91          if ( pos < 0 )
92          {
93              // We already have the key in the page : replace the value
94              // into a copy of this page, unless the page has already be copied
95              int index = -( pos + 1 );
96  
97              // Replace the existing value in a copy of the current page
98              InsertResult<K, V> result = replaceElement( revision, key, value, index );
99  
100             return result;
101         }
102 
103         // The key is not present in the leaf. We have to add it in the page
104         if ( nbElems < btree.getPageSize() )
105         {
106             // The current page is not full, it can contain the added element.
107             // We insert it into a copied page and return the result
108             Page<K, V> modifiedPage = addElement( revision, key, value, pos );
109 
110             InsertResult<K, V> result = new ModifyResult<K, V>( modifiedPage, null );
111             result.addCopiedPage( this );
112 
113             return result;
114         }
115         else
116         {
117             // The Page is already full : we split it and return the overflow element,
118             // after having created two pages.
119             InsertResult<K, V> result = addAndSplit( revision, key, value, pos );
120             result.addCopiedPage( this );
121 
122             return result;
123         }
124     }
125 
126 
127     /**
128      * {@inheritDoc}
129      */
130     @SuppressWarnings("unchecked")
131     public DeleteResult<K, V> delete( long revision, K key, V value, Page<K, V> parent, int parentPos )
132         throws IOException
133     {
134         // Check that the leaf is not empty
135         if ( nbElems == 0 )
136         {
137             // Empty leaf
138             return NotPresentResult.NOT_PRESENT;
139         }
140 
141         // Find the key in the page
142         int pos = findPos( key );
143 
144         if ( pos >= 0 )
145         {
146             // Not found : return the not present result.
147             return NotPresentResult.NOT_PRESENT;
148         }
149 
150         // Get the removed element
151         Tuple<K, V> removedElement = null;
152 
153         // flag to detect if a key was completely removed
154         boolean keyRemoved = false;
155 
156         int index = -( pos + 1 );
157 
158         if ( btree.isAllowDuplicates() )
159         {
160             BTree<V, V> dups = ( BTree<V, V> ) values[index].getValue( btree );
161 
162             if ( dups.hasKey( value ) )
163             {
164                 dups.delete( value );
165 
166                 if ( dups.getNbElems() == 0 )
167                 {
168                     keyRemoved = true;
169                 }
170 
171                 removedElement = new Tuple<K, V>( keys[index], value ); // we deleted only one value (even if it is from a tree of size 1)
172             }
173             else if ( value == null ) // this is a case to delete entire <K,dupsTree> 
174             {
175                 removedElement = new Tuple<K, V>( keys[index], ( V ) dups ); // the entire tree was removed so pass it as the value
176                 keyRemoved = true;
177             }
178             else
179             // value is not found
180             {
181                 return NotPresentResult.NOT_PRESENT;
182             }
183         }
184         else
185         {
186             V existing = values[index].getValue( btree );
187 
188             if ( ( ( existing == null ) && ( value == null ) ) || ( value == null ) )
189             {
190                 removedElement = new Tuple<K, V>( keys[index], existing );
191                 keyRemoved = true;
192             }
193             else if ( btree.getValueSerializer().compare( value, existing ) == 0 )
194             {
195                 removedElement = new Tuple<K, V>( keys[index], value );
196                 keyRemoved = true;
197             }
198             else
199             {
200                 return NotPresentResult.NOT_PRESENT;
201             }
202         }
203 
204         Leaf<K, V> newLeaf = null;
205 
206         if ( keyRemoved )
207         {
208             newLeaf = new Leaf<K, V>( btree, revision, nbElems - 1 );
209         }
210         else
211         {
212             newLeaf = new Leaf<K, V>( btree, revision, nbElems );
213         }
214 
215         // Create the result
216         DeleteResult<K, V> defaultResult = new RemoveResult<K, V>( newLeaf, removedElement );
217 
218         // If the parent is null, then this page is the root page.
219         if ( parent == null )
220         {
221             // Just remove the entry if it's present
222             copyAfterRemovingElement( keyRemoved, newLeaf, index );
223 
224             // The current page is added in the copied page list
225             defaultResult.addCopiedPage( this );
226 
227             return defaultResult;
228         }
229         else if ( keyRemoved )
230         {
231             // The current page is not the root. Check if the leaf has more than N/2
232             // elements
233             int halfSize = btree.getPageSize() / 2;
234 
235             if ( nbElems == halfSize )
236             {
237                 // We have to find a sibling now, and either borrow an entry from it
238                 // if it has more than N/2 elements, or to merge the two pages.
239                 // Check in both next and previous page, if they have the same parent
240                 // and select the biggest page with the same parent to borrow an element.
241                 int siblingPos = selectSibling( ( Node<K, V> ) parent, parentPos );
242                 Leaf<K, V> sibling = ( Leaf<K, V> ) ( ( ( Node<K, V> ) parent ).children[siblingPos].getValue( btree ) );
243 
244                 if ( sibling.getNbElems() == halfSize )
245                 {
246                     // We will merge the current page with its sibling
247                     DeleteResult<K, V> result = mergeWithSibling( removedElement, revision, sibling,
248                         ( siblingPos < parentPos ), index );
249 
250                     return result;
251                 }
252                 else
253                 {
254                     // We can borrow the element from the left sibling
255                     if ( siblingPos < parentPos )
256                     {
257                         DeleteResult<K, V> result = borrowFromLeft( removedElement, revision, sibling, index );
258 
259                         return result;
260                     }
261                     else
262                     {
263                         // Borrow from the right sibling
264                         DeleteResult<K, V> result = borrowFromRight( removedElement, revision, sibling, index );
265 
266                         return result;
267                     }
268                 }
269             }
270             else
271             {
272                 // The page has more than N/2 elements.
273                 // We simply remove the element from the page, and if it was the leftmost,
274                 // we return the new pivot (it will replace any instance of the removed
275                 // key in its parents)
276                 copyAfterRemovingElement( keyRemoved, newLeaf, index );
277 
278                 // The current page is added in the copied page list
279                 defaultResult.addCopiedPage( this );
280 
281                 return defaultResult;
282             }
283         }
284         else
285         {
286             // Last, not least : we can copy the full page
287             // Copy the keys and the values
288             System.arraycopy( keys, 0, newLeaf.keys, 0, nbElems );
289             System.arraycopy( values, 0, newLeaf.values, 0, nbElems );
290 
291             // The current page is added in the copied page list
292             defaultResult.addCopiedPage( this );
293 
294             return defaultResult;
295         }
296     }
297 
298 
299     /**
300      * Merges the sibling with the current leaf, after having removed the element in the page.
301      * 
302      * @param revision The new revision
303      * @param sibling The sibling we will merge with
304      * @param isLeft Tells if the sibling is on the left or on the right
305      * @param pos The position of the removed element
306      * @return The new created leaf containing the sibling and the old page.
307      * @throws IOException If we have an error while trying to access the page
308      */
309     private DeleteResult<K, V> mergeWithSibling( Tuple<K, V> removedElement, long revision, Leaf<K, V> sibling,
310         boolean isLeft, int pos )
311         throws EndOfFileExceededException, IOException
312     {
313         // Create the new page. It will contain N - 1 elements (the maximum number)
314         // as we merge two pages that contain N/2 elements minus the one we remove
315         Leaf<K, V> newLeaf = new Leaf<K, V>( btree, revision, btree.getPageSize() - 1 );
316 
317         if ( isLeft )
318         {
319             // The sibling is on the left
320             // Copy all the elements from the sibling first
321             System.arraycopy( sibling.keys, 0, newLeaf.keys, 0, sibling.nbElems );
322             System.arraycopy( sibling.values, 0, newLeaf.values, 0, sibling.nbElems );
323 
324             // Copy all the elements from the page up to the deletion position
325             System.arraycopy( keys, 0, newLeaf.keys, sibling.nbElems, pos );
326             System.arraycopy( values, 0, newLeaf.values, sibling.nbElems, pos );
327 
328             // And copy the remaining elements after the deletion point
329             System.arraycopy( keys, pos + 1, newLeaf.keys, sibling.nbElems + pos, nbElems - pos - 1 );
330             System.arraycopy( values, pos + 1, newLeaf.values, sibling.nbElems + pos, nbElems - pos - 1 );
331         }
332         else
333         {
334             // The sibling is on the right
335             // Copy all the elements from the page up to the deletion position
336             System.arraycopy( keys, 0, newLeaf.keys, 0, pos );
337             System.arraycopy( values, 0, newLeaf.values, 0, pos );
338 
339             // Then copy the remaining elements after the deletion point
340             System.arraycopy( keys, pos + 1, newLeaf.keys, pos, nbElems - pos - 1 );
341             System.arraycopy( values, pos + 1, newLeaf.values, pos, nbElems - pos - 1 );
342 
343             // And copy all the elements from the sibling
344             System.arraycopy( sibling.keys, 0, newLeaf.keys, nbElems - 1, sibling.nbElems );
345             System.arraycopy( sibling.values, 0, newLeaf.values, nbElems - 1, sibling.nbElems );
346         }
347 
348         // And create the result
349         DeleteResult<K, V> result = new MergedWithSiblingResult<K, V>( newLeaf,
350             removedElement );
351 
352         result.addCopiedPage( this );
353         result.addCopiedPage( sibling );
354 
355         return result;
356     }
357 
358 
359     /**
360      * Borrows an element from the left sibling, creating a new sibling with one
361      * less element and creating a new page where the element to remove has been
362      * deleted and the borrowed element added on the left.
363      * 
364      * @param revision The new revision for all the pages
365      * @param sibling The left sibling
366      * @param pos The position of the element to remove
367      * @return The resulting pages
368      * @throws IOException If we have an error while trying to access the page 
369      */
370     private DeleteResult<K, V> borrowFromLeft( Tuple<K, V> removedElement, long revision, Leaf<K, V> sibling, int pos )
371         throws IOException
372     {
373         // The sibling is on the left, borrow the rightmost element
374         K siblingKey = sibling.keys[sibling.getNbElems() - 1];
375         ElementHolder<V, K, V> siblingValue = sibling.values[sibling.getNbElems() - 1];
376 
377         // Create the new sibling, with one less element at the end
378         Leaf<K, V> newSibling = ( Leaf<K, V> ) sibling.copy( revision, sibling.getNbElems() - 1 );
379 
380         // Create the new page and add the new element at the beginning
381         // First copy the current page, with the same size
382         Leaf<K, V> newLeaf = new Leaf<K, V>( btree, revision, nbElems );
383 
384         // Insert the borrowed element
385         newLeaf.keys[0] = siblingKey;
386         newLeaf.values[0] = siblingValue;
387 
388         // Copy the keys and the values up to the insertion position,
389         System.arraycopy( keys, 0, newLeaf.keys, 1, pos );
390         System.arraycopy( values, 0, newLeaf.values, 1, pos );
391 
392         // And copy the remaining elements
393         System.arraycopy( keys, pos + 1, newLeaf.keys, pos + 1, keys.length - pos - 1 );
394         System.arraycopy( values, pos + 1, newLeaf.values, pos + 1, values.length - pos - 1 );
395 
396         DeleteResult<K, V> result = new BorrowedFromLeftResult<K, V>( newLeaf, newSibling, removedElement );
397 
398         // Add the copied pages to the list
399         result.addCopiedPage( this );
400         result.addCopiedPage( sibling );
401 
402         return result;
403     }
404 
405 
406     /**
407      * Borrows an element from the right sibling, creating a new sibling with one
408      * less element and creating a new page where the element to remove has been
409      * deleted and the borrowed element added on the right.
410      * 
411      * @param revision The new revision for all the pages
412      * @param sibling The right sibling
413      * @param pos The position of the element to remove
414      * @return The resulting pages
415      * @throws IOException If we have an error while trying to access the page 
416      */
417     private DeleteResult<K, V> borrowFromRight( Tuple<K, V> removedElement, long revision, Leaf<K, V> sibling, int pos )
418         throws IOException
419     {
420         // The sibling is on the left, borrow the rightmost element
421         K siblingKey = sibling.keys[0];
422         ElementHolder<V, K, V> siblingHolder = sibling.values[0];
423 
424         // Create the new sibling
425         Leaf<K, V> newSibling = new Leaf<K, V>( btree, revision, sibling.getNbElems() - 1 );
426 
427         // Copy the keys and the values from 1 to N in the new sibling
428         System.arraycopy( sibling.keys, 1, newSibling.keys, 0, sibling.nbElems - 1 );
429         System.arraycopy( sibling.values, 1, newSibling.values, 0, sibling.nbElems - 1 );
430 
431         // Create the new page and add the new element at the end
432         // First copy the current page, with the same size
433         Leaf<K, V> newLeaf = new Leaf<K, V>( btree, revision, nbElems );
434 
435         // Insert the borrowed element at the end
436         newLeaf.keys[nbElems - 1] = siblingKey;
437         newLeaf.values[nbElems - 1] = siblingHolder;
438 
439         // Copy the keys and the values up to the deletion position,
440         System.arraycopy( keys, 0, newLeaf.keys, 0, pos );
441         System.arraycopy( values, 0, newLeaf.values, 0, pos );
442 
443         // And copy the remaining elements
444         System.arraycopy( keys, pos + 1, newLeaf.keys, pos, keys.length - pos - 1 );
445         System.arraycopy( values, pos + 1, newLeaf.values, pos, values.length - pos - 1 );
446 
447         DeleteResult<K, V> result = new BorrowedFromRightResult<K, V>( newLeaf, newSibling, removedElement );
448 
449         // Add the copied pages to the list
450         result.addCopiedPage( this );
451         result.addCopiedPage( sibling );
452 
453         return result;
454     }
455 
456 
457     /**
458      * Copies the elements of the current page to a new page
459      *
460      * @param keyRemoved a flag stating if the key was removed
461      * @param newLeaf The new page into which the remaining keys and values will be copied
462      * @param pos The position into the page of the element to remove
463      * @throws IOException If we have an error while trying to access the page
464      */
465     private void copyAfterRemovingElement( boolean keyRemoved, Leaf<K, V> newLeaf, int pos ) throws IOException
466     {
467         if ( keyRemoved )
468         {
469             // Deal with the special case of a page with only one element by skipping
470             // the copy, as we won't have any remaining  element in the page
471             if ( nbElems == 1 )
472             {
473                 return;
474             }
475 
476             // Copy the keys and the values up to the insertion position
477             System.arraycopy( keys, 0, newLeaf.keys, 0, pos );
478             System.arraycopy( values, 0, newLeaf.values, 0, pos );
479 
480             // And copy the elements after the position
481             System.arraycopy( keys, pos + 1, newLeaf.keys, pos, keys.length - pos - 1 );
482             System.arraycopy( values, pos + 1, newLeaf.values, pos, values.length - pos - 1 );
483         }
484         else
485         // one of the many values of the same key was removed, no change in the number of keys
486         {
487             System.arraycopy( keys, 0, newLeaf.keys, 0, nbElems );
488             System.arraycopy( values, 0, newLeaf.values, 0, nbElems );
489         }
490     }
491 
492 
493     /**
494      * {@inheritDoc}
495      */
496     public V get( K key ) throws KeyNotFoundException, IOException
497     {
498         int pos = findPos( key );
499 
500         if ( pos < 0 )
501         {
502             V v = values[-( pos + 1 )].getValue( btree );
503 
504             if ( btree.isAllowDuplicates() )
505             {
506                 // always return the first value for get(key) when duplicates are allowed
507                 BTree<V, V> dupTree = ( ( BTree<V, V> ) v );
508                 return dupTree.rootPage.getLeftMostKey();
509             }
510 
511             return v;
512         }
513         else
514         {
515             throw new KeyNotFoundException( "Cannot find an entry for key " + key );
516         }
517     }
518 
519 
520     /**
521      * {@inheritDoc}
522      */
523     @Override
524     public BTree<V, V> getValues( K key ) throws KeyNotFoundException, IOException, IllegalArgumentException
525     {
526         if ( !btree.isAllowDuplicates() )
527         {
528             throw new IllegalArgumentException( "Duplicates are not allowed in this tree" );
529         }
530 
531         int pos = findPos( key );
532 
533         if ( pos < 0 )
534         {
535             V v = values[-( pos + 1 )].getValue( btree );
536 
537             return ( ( BTree<V, V> ) v );
538         }
539         else
540         {
541             throw new KeyNotFoundException( "Cannot find an entry for key " + key );
542         }
543     }
544 
545 
546     /**
547      * {@inheritDoc}
548      */
549     public boolean hasKey( K key )
550     {
551         int pos = findPos( key );
552 
553         if ( pos < 0 )
554         {
555             return true;
556         }
557 
558         return false;
559     }
560 
561 
562     @Override
563     public boolean contains( K key, V value ) throws IOException
564     {
565         int pos = findPos( key );
566 
567         if ( pos < 0 )
568         {
569             V v = values[-( pos + 1 )].getValue( btree );
570 
571             if ( btree.isAllowDuplicates() )
572             {
573                 // always return the first value for get(key) when duplicates are allowed
574                 BTree<V, V> dupTree = ( ( BTree<V, V> ) v );
575                 return dupTree.hasKey( value );
576             }
577             else
578             {
579                 return ( btree.getValueSerializer().compare( value, v ) == 0 );
580             }
581         }
582         else
583         {
584             return false;
585         }
586     }
587 
588 
589     /**
590      * {@inheritDoc}
591      */
592     public ElementHolder<V, K, V> getValue( int pos )
593     {
594         if ( pos < nbElems )
595         {
596             return values[pos];
597         }
598         else
599         {
600             return null;
601         }
602     }
603 
604 
605     /**
606      * Sets the value at a give position
607      * @param pos The position in the values array
608      * @param value the value to inject
609      */
610     public void setValue( int pos, ElementHolder<V, K, V> value )
611     {
612         values[pos] = value;
613     }
614 
615 
616     /**
617      * {@inheritDoc}
618      */
619     public Cursor<K, V> browse( K key, Transaction<K, V> transaction, LinkedList<ParentPos<K, V>> stack )
620     {
621         int pos = findPos( key );
622         Cursor<K, V> cursor = null;
623 
624         if ( pos < 0 )
625         {
626             int index = -( pos + 1 );
627 
628             // The first element has been found. Create the cursor
629             ParentPos<K, V> parentPos = new ParentPos<K, V>( this, index );
630             setDupsContainer( parentPos, btree );
631             stack.push( parentPos );
632 
633             cursor = new Cursor<K, V>( btree, transaction, stack );
634         }
635         else
636         {
637             // The key has not been found. Select the value just above, if we have one
638             if ( pos < nbElems )
639             {
640                 ParentPos<K, V> parentPos = new ParentPos<K, V>( this, pos );
641                 setDupsContainer( parentPos, btree );
642                 stack.push( parentPos );
643 
644                 cursor = new Cursor<K, V>( btree, transaction, stack );
645             }
646             else
647             {
648                 // Not found : return a null cursor
649                 stack.push( new ParentPos<K, V>( null, -1 ) );
650 
651                 return new Cursor<K, V>( btree, transaction, stack );
652             }
653         }
654 
655         return cursor;
656     }
657 
658 
659     /**
660      * {@inheritDoc}
661      */
662     public Cursor<K, V> browse( Transaction<K, V> transaction, LinkedList<ParentPos<K, V>> stack )
663     {
664         int pos = 0;
665         Cursor<K, V> cursor = null;
666 
667         if ( nbElems == 0 )
668         {
669             // The tree is empty, it's the root, we have nothing to return
670             stack.push( new ParentPos<K, V>( null, -1 ) );
671 
672             return new Cursor<K, V>( btree, transaction, stack );
673         }
674         else
675         {
676             // Start at the beginning of the page
677             ParentPos<K, V> parentPos = new ParentPos<K, V>( this, pos );
678 
679             setDupsContainer( parentPos, btree );
680 
681             stack.push( parentPos );
682 
683             cursor = new Cursor<K, V>( btree, transaction, stack );
684         }
685 
686         return cursor;
687     }
688 
689 
690     /**
691      * Copy the current page and all of the keys, values and children, if it's not a leaf.
692      * 
693      * @param revision The new revision
694      * @param nbElems The number of elements to copy
695      * @return The copied page
696      */
697     private Page<K, V> copy( long revision, int nbElems )
698     {
699         Leaf<K, V> newLeaf = new Leaf<K, V>( btree, revision, nbElems );
700 
701         // Copy the keys and the values
702         System.arraycopy( keys, 0, newLeaf.keys, 0, nbElems );
703         System.arraycopy( values, 0, newLeaf.values, 0, nbElems );
704 
705         return newLeaf;
706     }
707 
708 
709     /**
710      * Copy the current page if needed, and replace the value at the position we have found the key.
711      * 
712      * @param revision The new page revision
713      * @param key The new key
714      * @param value the new value
715      * @param pos The position of the key in the page
716      * @return The copied page
717      * @throws IOException If we have an error while trying to access the page
718      */
719     private InsertResult<K, V> replaceElement( long revision, K key, V value, int pos )
720         throws IOException
721     {
722         Leaf<K, V> newLeaf = this;
723 
724         if ( this.revision != revision )
725         {
726             // The page hasn't been modified yet, we need to copy it first
727             newLeaf = ( Leaf<K, V> ) copy( revision, nbElems );
728         }
729 
730         V oldValue = null;
731 
732         if ( btree.isAllowDuplicates() )
733         {
734             BTree<V, V> dupValues = ( BTree<V, V> ) newLeaf.values[pos].getValue( btree );
735 
736             // return value will always be null  here 
737             if ( !dupValues.hasKey( value ) )
738             {
739                 dupValues.insert( value, null, 0 );
740             }
741             else
742             {
743                 oldValue = value;
744             }
745         }
746         else
747         {
748             // Now we can inject the value
749             oldValue = newLeaf.values[pos].getValue( btree );
750             newLeaf.values[pos] = btree.createHolder( value );
751         }
752 
753         // Create the result
754         InsertResult<K, V> result = new ModifyResult<K, V>( newLeaf, oldValue );
755         result.addCopiedPage( this );
756 
757         return result;
758     }
759 
760 
761     /**
762      * Adds a new <K, V> into a copy of the current page at a given position. We return the
763      * modified page. The new page will have one more element than the current page.
764      * 
765      * @param revision The revision of the modified page
766      * @param key The key to insert
767      * @param value The value to insert
768      * @param pos The position into the page
769      * @return The modified page with the <K,V> element added
770      */
771     private Page<K, V> addElement( long revision, K key, V value, int pos )
772     {
773         // First copy the current page, but add one element in the copied page
774         Leaf<K, V> newLeaf = new Leaf<K, V>( btree, revision, nbElems + 1 );
775 
776         // Atm, store the value in memory
777 
778         ElementHolder valueHolder = null;
779 
780         if ( btree.isAllowDuplicates() )
781         {
782             valueHolder = new DuplicateKeyMemoryHolder<K, V>( btree, value );
783         }
784         else
785         {
786             valueHolder = new MemoryHolder<K, V>( btree, value );
787         }
788 
789         //ValueHolder<K, V> valueHolder = btree.createHolder( value );
790 
791         // Deal with the special case of an empty page
792         if ( nbElems == 0 )
793         {
794             newLeaf.keys[0] = key;
795 
796             newLeaf.values[0] = valueHolder;
797         }
798         else
799         {
800             //FIXME do not copy the duplicate key's value container, this will improve the perf significantly
801             // Copy the keys and the values up to the insertion position
802             System.arraycopy( keys, 0, newLeaf.keys, 0, pos );
803             System.arraycopy( values, 0, newLeaf.values, 0, pos );
804 
805             // Add the new element
806             newLeaf.keys[pos] = key;
807             newLeaf.values[pos] = valueHolder;
808 
809             // And copy the remaining elements
810             System.arraycopy( keys, pos, newLeaf.keys, pos + 1, keys.length - pos );
811             System.arraycopy( values, pos, newLeaf.values, pos + 1, values.length - pos );
812         }
813 
814         return newLeaf;
815     }
816 
817 
818     /**
819      * Split a full page into two new pages, a left, a right and a pivot element. The new pages will
820      * each contains half of the original elements. <br/>
821      * The pivot will be computed, depending on the place
822      * we will inject the newly added element. <br/>
823      * If the newly added element is in the middle, we will use it
824      * as a pivot. Otherwise, we will use either the last element in the left page if the element is added
825      * on the left, or the first element in the right page if it's added on the right.
826      * 
827      * @param revision The new revision for all the created pages
828      * @param key The key to add
829      * @param value The value to add
830      * @param pos The position of the insertion of the new element
831      * @return An OverflowPage containing the pivot, and the new left and right pages
832      */
833     private InsertResult<K, V> addAndSplit( long revision, K key, V value, int pos )
834     {
835         int middle = btree.getPageSize() >> 1;
836         Leaf<K, V> leftLeaf = null;
837         Leaf<K, V> rightLeaf = null;
838         ElementHolder<V, K, V> valueHolder = btree.createHolder( value );
839 
840         // Determinate where to store the new value
841         if ( pos <= middle )
842         {
843             // The left page will contain the new value
844             leftLeaf = new Leaf<K, V>( btree, revision, middle + 1 );
845 
846             // Copy the keys and the values up to the insertion position
847             System.arraycopy( keys, 0, leftLeaf.keys, 0, pos );
848             System.arraycopy( values, 0, leftLeaf.values, 0, pos );
849 
850             // Add the new element
851             leftLeaf.keys[pos] = key;
852             leftLeaf.values[pos] = valueHolder;
853 
854             // And copy the remaining elements
855             System.arraycopy( keys, pos, leftLeaf.keys, pos + 1, middle - pos );
856             System.arraycopy( values, pos, leftLeaf.values, pos + 1, middle - pos );
857 
858             // Now, create the right page
859             rightLeaf = new Leaf<K, V>( btree, revision, middle );
860 
861             // Copy the keys and the values in the right page
862             System.arraycopy( keys, middle, rightLeaf.keys, 0, middle );
863             System.arraycopy( values, middle, rightLeaf.values, 0, middle );
864         }
865         else
866         {
867             // Create the left page
868             leftLeaf = new Leaf<K, V>( btree, revision, middle );
869 
870             // Copy all the element into the left page
871             System.arraycopy( keys, 0, leftLeaf.keys, 0, middle );
872             System.arraycopy( values, 0, leftLeaf.values, 0, middle );
873 
874             // Now, create the right page
875             rightLeaf = new Leaf<K, V>( btree, revision, middle + 1 );
876 
877             int rightPos = pos - middle;
878 
879             // Copy the keys and the values up to the insertion position
880             System.arraycopy( keys, middle, rightLeaf.keys, 0, rightPos );
881             System.arraycopy( values, middle, rightLeaf.values, 0, rightPos );
882 
883             // Add the new element
884             rightLeaf.keys[rightPos] = key;
885             rightLeaf.values[rightPos] = valueHolder;
886 
887             // And copy the remaining elements
888             System.arraycopy( keys, pos, rightLeaf.keys, rightPos + 1, nbElems - pos );
889             System.arraycopy( values, pos, rightLeaf.values, rightPos + 1, nbElems - pos );
890         }
891 
892         // Get the pivot
893         K pivot = rightLeaf.keys[0];
894 
895         // Create the result
896         InsertResult<K, V> result = new SplitResult<K, V>( pivot, leftLeaf, rightLeaf );
897 
898         return result;
899     }
900 
901 
902     /**
903      * {@inheritDoc}
904      */
905     public K getLeftMostKey()
906     {
907         return keys[0];
908     }
909 
910 
911     /**
912      * {@inheritDoc}
913      */
914     public K getRightMostKey()
915     {
916         return keys[nbElems - 1];
917     }
918 
919 
920     /**
921      * {@inheritDoc}
922      */
923     public Tuple<K, V> findLeftMost() throws IOException
924     {
925         V val = null;
926 
927         if ( btree.isAllowDuplicates() )
928         {
929             BTree<V, V> dupTree = ( BTree<V, V> ) values[0].getValue( btree );
930             val = dupTree.rootPage.getLeftMostKey();
931         }
932         else
933         {
934             val = values[0].getValue( btree );
935         }
936 
937         return new Tuple<K, V>( keys[0], val );
938     }
939 
940 
941     /**
942      * {@inheritDoc}
943      */
944     public Tuple<K, V> findRightMost() throws EndOfFileExceededException, IOException
945     {
946         V val = null;
947 
948         if ( btree.isAllowDuplicates() )
949         {
950             BTree<V, V> dupTree = ( BTree<V, V> ) values[nbElems - 1].getValue( btree );
951             val = dupTree.rootPage.getRightMostKey();
952         }
953         else
954         {
955             val = values[nbElems - 1].getValue( btree );
956         }
957 
958         return new Tuple<K, V>( keys[nbElems - 1], val );
959     }
960 
961 
962     /**
963      * @see Object#toString()
964      */
965     public String toString()
966     {
967         StringBuilder sb = new StringBuilder();
968 
969         sb.append( "Leaf[" );
970         sb.append( super.toString() );
971 
972         sb.append( "] -> {" );
973 
974         if ( nbElems > 0 )
975         {
976             boolean isFirst = true;
977 
978             for ( int i = 0; i < nbElems; i++ )
979             {
980                 if ( isFirst )
981                 {
982                     isFirst = false;
983                 }
984                 else
985                 {
986                     sb.append( ", " );
987                 }
988 
989                 sb.append( "<" ).append( keys[i] ).append( "," ).append( values[i] ).append( ">" );
990             }
991         }
992 
993         sb.append( "}" );
994 
995         return sb.toString();
996     }
997 
998 
999     /**
1000      * {@inheritDoc}
1001      */
1002     public String dumpPage( String tabs )
1003     {
1004         StringBuilder sb = new StringBuilder();
1005 
1006         sb.append( tabs );
1007 
1008         if ( nbElems > 0 )
1009         {
1010             boolean isFirst = true;
1011 
1012             for ( int i = 0; i < nbElems; i++ )
1013             {
1014                 if ( isFirst )
1015                 {
1016                     isFirst = false;
1017                 }
1018                 else
1019                 {
1020                     sb.append( ", " );
1021                 }
1022 
1023                 sb.append( "<" ).append( keys[i] ).append( "," ).append( values[i] ).append( ">" );
1024             }
1025         }
1026 
1027         sb.append( "\n" );
1028 
1029         return sb.toString();
1030     }
1031 }