View Javadoc

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