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