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