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.EndOfFileExceededException;
27  import org.apache.directory.mavibot.btree.exception.KeyNotFoundException;
28  
29  
30  /**
31   * A MVCC abstract Page. It stores the field and the methods shared by the Node and Leaf
32   * classes.
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*/abstract class AbstractPage<K, V> implements Page<K, V>
40  {
41      /** Parent B+Tree. */
42      protected transient BTree<K, V> btree;
43  
44      /** Keys of children nodes */
45      protected KeyHolder<K>[] keys;
46  
47      /** Children pages associated with keys. */
48      protected PageHolder<K, V>[] children;
49  
50      /** The number of current values in the Page */
51      protected int nbElems;
52  
53      /** This BPage's revision */
54      protected long revision;
55  
56      /** The first {@link PageIO} storing the serialized Page on disk */
57      protected long offset = -1L;
58  
59      /** The last {@link PageIO} storing the serialized Page on disk */
60      protected long lastOffset = -1L;
61  
62      /**
63       * Creates a default empty AbstractPage
64       *
65       * @param btree The associated BTree
66       */
67      protected AbstractPage( BTree<K, V> btree )
68      {
69          this.btree = btree;
70      }
71  
72  
73      /**
74       * Internal constructor used to create Page instance used when a page is being copied or overflow
75       */
76      @SuppressWarnings("unchecked")
77      // Cannot create an array of generic objects
78      protected AbstractPage( BTree<K, V> btree, long revision, int nbElems )
79      {
80          this.btree = btree;
81          this.revision = revision;
82          this.nbElems = nbElems;
83          this.keys = ( KeyHolder[] ) Array.newInstance( KeyHolder.class, nbElems );
84      }
85  
86  
87      /**
88       * {@inheritDoc}
89       */
90      public int getNbElems()
91      {
92          return nbElems;
93      }
94  
95  
96      /**
97       * Sets the number of element in this page
98       * @param nbElems The number of elements
99       */
100     /* no qualifier */void setNbElems( int nbElems )
101     {
102         this.nbElems = nbElems;
103     }
104 
105 
106     /**
107      * {@inheritDoc}
108      */
109     public K getKey( int pos )
110     {
111         if ( ( pos < nbElems ) && ( keys[pos] != null ) )
112         {
113             return keys[pos].getKey();
114         }
115         else
116         {
117             return null;
118         }
119     }
120 
121 
122     /**
123      * {@inheritDoc}
124      */
125     @Override
126     public boolean hasKey( K key ) throws IOException
127     {
128         int pos = findPos( key );
129 
130         if ( pos < 0 )
131         {
132             // Here, if we have found the key in the node, then we must go down into
133             // the right child, not the left one
134             return children[-pos].getValue().hasKey( key );
135         }
136         else
137         {
138             Page<K, V> page = children[pos].getValue();
139 
140             if ( page == null )
141             {
142                 System.out.println( "Page is null for pos = " + pos + ", children = " + children[pos] );
143             }
144 
145             return page.hasKey( key );
146         }
147     }
148 
149 
150     /**
151      * {@inheritDoc}
152      */
153     /* no qualifier */Page<K, V> getReference( int pos ) throws IOException
154     {
155         if ( pos < nbElems + 1 )
156         {
157             return children[pos].getValue();
158         }
159         else
160         {
161             return null;
162         }
163     }
164 
165 
166     /**
167      * {@inheritDoc}
168      */
169     public TupleCursor<K, V> browse( K key, ReadTransaction<K, V> transaction, ParentPos<K, V>[] stack, int depth )
170         throws IOException
171     {
172         int pos = findPos( key );
173 
174         if ( pos < 0 )
175         {
176             pos = -pos;
177         }
178 
179         // We first stack the current page
180         stack[depth++] = new ParentPos<K, V>( this, pos );
181 
182         Page<K, V> page = children[pos].getValue();
183 
184         return page.browse( key, transaction, stack, depth );
185     }
186 
187 
188     /**
189      * {@inheritDoc}
190      */
191     @Override
192     public boolean contains( K key, V value ) throws IOException
193     {
194         int pos = findPos( key );
195 
196         if ( pos < 0 )
197         {
198             // Here, if we have found the key in the node, then we must go down into
199             // the right child, not the left one
200             return children[-pos].getValue().contains( key, value );
201         }
202         else
203         {
204             return children[pos].getValue().contains( key, value );
205         }
206     }
207 
208 
209     /**
210      * {@inheritDoc}
211      */
212     public DeleteResult<K, V> delete( K key, V value, long revision ) throws IOException
213     {
214         return delete( key, value, revision, null, -1 );
215     }
216 
217 
218     /**
219      * The real delete implementation. It can be used for internal deletion in the B-tree.
220      *
221      * @param key The key to delete
222      * @param value The value to delete
223      * @param revision The revision for which we want to delete a tuple
224      * @param parent The parent page
225      * @param parentPos The position of this page in the parent page
226      * @return The result
227      * @throws IOException If we had an issue while processing the deletion
228      */
229     /* no qualifier */abstract DeleteResult<K, V> delete( K key, V value, long revision, Page<K, V> parent, int parentPos )
230         throws IOException;
231 
232 
233     /**
234      * {@inheritDoc}
235      */
236     public V get( K key ) throws IOException, KeyNotFoundException
237     {
238         int pos = findPos( key );
239 
240         if ( pos < 0 )
241         {
242             // Here, if we have found the key in the node, then we must go down into
243             // the right child, not the left one
244             return children[-pos].getValue().get( key );
245         }
246         else
247         {
248             return children[pos].getValue().get( key );
249         }
250     }
251 
252 
253     /**
254      * {@inheritDoc}
255      */
256     /* no qualifier */Page<K, V> getPage( int pos )
257     {
258         if ( ( pos >= 0 ) && ( pos < children.length ) )
259         {
260             return children[pos].getValue();
261         }
262         else
263         {
264             return null;
265         }
266     }
267 
268 
269     /**
270      * {@inheritDoc}
271      */
272     /* no qualifier */void setPageHolder( int pos, PageHolder<K, V> pageHolder )
273     {
274         if ( ( pos >= 0 ) && ( pos < children.length ) )
275         {
276             children[pos] = pageHolder;
277         }
278     }
279 
280 
281     /**
282      * {@inheritDoc}
283      */
284     @Override
285     public ValueCursor<V> getValues( K key ) throws KeyNotFoundException, IOException, IllegalArgumentException
286     {
287         int pos = findPos( key );
288 
289         if ( pos < 0 )
290         {
291             // Here, if we have found the key in the node, then we must go down into
292             // the right child, not the left one
293             return children[-pos].getValue().getValues( key );
294         }
295         else
296         {
297             return children[pos].getValue().getValues( key );
298         }
299     }
300 
301 
302     /**
303      * {@inheritDoc}
304      */
305     public TupleCursor<K, V> browse( ReadTransaction<K, V> transaction, ParentPos<K, V>[] stack, int depth )
306         throws IOException
307     {
308         stack[depth++] = new ParentPos<K, V>( this, 0 );
309 
310         Page<K, V> page = children[0].getValue();
311 
312         return page.browse( transaction, stack, depth );
313     }
314 
315 
316     /**
317      * {@inheritDoc}
318      */
319     public KeyCursor<K> browseKeys( ReadTransaction<K, K> transaction, ParentPos<K, K>[] stack, int depth )
320         throws IOException
321     {
322         stack[depth++] = new ParentPos( this, 0 );
323 
324         Page<K, V> page = children[0].getValue();
325 
326         return page.browseKeys( transaction, stack, depth );
327     }
328 
329     
330     /**
331      * Selects the sibling (the previous or next page with the same parent) which has
332      * the more element assuming it's above N/2
333      *
334      * @param parent The parent of the current page
335      * @param The position of the current page reference in its parent
336      * @return The position of the sibling, or -1 if we have'nt found any sibling
337      * @throws IOException If we have an error while trying to access the page
338      */
339     protected int selectSibling( Page<K, V> parent, int parentPos ) throws IOException
340     {
341         if ( parentPos == 0 )
342         {
343             // The current page is referenced on the left of its parent's page :
344             // we will not have a previous page with the same parent
345             return 1;
346         }
347 
348         if ( parentPos == parent.getNbElems() )
349         {
350             // The current page is referenced on the right of its parent's page :
351             // we will not have a next page with the same parent
352             return parentPos - 1;
353         }
354 
355         Page<K, V> prevPage = ( ( AbstractPage<K, V> ) parent ).getPage( parentPos - 1 );
356         Page<K, V> nextPage = ( ( AbstractPage<K, V> ) parent ).getPage( parentPos + 1 );
357 
358         int prevPageSize = prevPage.getNbElems();
359         int nextPageSize = nextPage.getNbElems();
360 
361         if ( prevPageSize >= nextPageSize )
362         {
363             return parentPos - 1;
364         }
365         else
366         {
367             return parentPos + 1;
368         }
369     }
370 
371 
372     /**
373      * {@inheritDoc}
374      */
375     public K getLeftMostKey()
376     {
377         return keys[0].getKey();
378     }
379 
380 
381     /**
382      * {@inheritDoc}
383      */
384     public K getRightMostKey()
385     {
386         return keys[nbElems - 1].getKey();
387     }
388 
389 
390     /**
391      * @return the offset of the first {@link PageIO} which stores the Page on disk.
392      */
393     /* no qualifier */long getOffset()
394     {
395         return offset;
396     }
397 
398 
399     /**
400      * @param offset the offset to set
401      */
402     /* no qualifier */void setOffset( long offset )
403     {
404         this.offset = offset;
405     }
406 
407 
408     /**
409      * @return the offset of the last {@link PageIO} which stores the Page on disk.
410      */
411     /* no qualifier */long getLastOffset()
412     {
413         return lastOffset;
414     }
415 
416 
417     /**
418      * {@inheritDoc}
419      */
420     /* no qualifier */void setLastOffset( long lastOffset )
421     {
422         this.lastOffset = lastOffset;
423     }
424 
425 
426     /**
427      * @return the keys
428      */
429     /* no qualifier */KeyHolder<K>[] getKeys()
430     {
431         return keys;
432     }
433 
434 
435     /**
436      * Sets the key at a give position
437      *
438      * @param pos The position in the keys array
439      * @param key the key to inject
440      */
441     /* no qualifier */void setKey( int pos, KeyHolder<K> key )
442     {
443         keys[pos] = key;
444     }
445 
446 
447     /**
448      * @param revision the keys to set
449      */
450     /* no qualifier */void setKeys( KeyHolder<K>[] keys )
451     {
452         this.keys = keys;
453     }
454 
455 
456     /**
457      * {@inheritDoc}
458      */
459     /* no qualifier */ValueHolder<V> getValue( int pos )
460     {
461         // Node don't have values. Leaf.getValue() will return the value
462         return null;
463     }
464 
465 
466     /**
467      * @return the revision
468      */
469     public long getRevision()
470     {
471         return revision;
472     }
473 
474 
475     /**
476      * @param revision the revision to set
477      */
478     /* no qualifier */void setRevision( long revision )
479     {
480         this.revision = revision;
481     }
482 
483 
484     /**
485      * Compares two keys
486      *
487      * @param key1 The first key
488      * @param key2 The second key
489      * @return -1 if the first key is above the second one, 1 if it's below, and 0
490      * if the two keys are equal
491      */
492     protected final int compare( K key1, K key2 )
493     {
494         if ( key1 == key2 )
495         {
496             return 0;
497         }
498 
499         if ( key1 == null )
500         {
501             return 1;
502         }
503 
504         if ( key2 == null )
505         {
506             return -1;
507         }
508 
509         return btree.getComparator().compare( key1, key2 );
510     }
511 
512 
513     /**
514      * Finds the position of the given key in the page. If we have found the key,
515      * we will return its position as a negative value.
516      * <p/>
517      * Assuming that the array is zero-indexed, the returned value will be : <br/>
518      *   position = - ( position + 1)
519      * <br/>
520      * So for the following table of keys : <br/>
521      * <pre>
522      * +---+---+---+---+
523      * | b | d | f | h |
524      * +---+---+---+---+
525      *   0   1   2   3
526      * </pre>
527      * looking for 'b' will return -1 (-(0+1)) and looking for 'f' will return -3 (-(2+1)).<br/>
528      * Computing the real position is just a matter to get -(position++).
529      * <p/>
530      * If we don't find the key in the table, we will return the position of the key
531      * immediately above the key we are looking for. <br/>
532      * For instance, looking for :
533      * <ul>
534      * <li>'a' will return 0</li>
535      * <li>'b' will return -1</li>
536      * <li>'c' will return 1</li>
537      * <li>'d' will return -2</li>
538      * <li>'e' will return 2</li>
539      * <li>'f' will return -3</li>
540      * <li>'g' will return 3</li>
541      * <li>'h' will return -4</li>
542      * <li>'i' will return 4</li>
543      * </ul>
544      *
545      *
546      * @param key The key to find
547      * @return The position in the page.
548      */
549     public int findPos( K key )
550     {
551         // Deal with the special key where we have an empty page
552         if ( nbElems == 0 )
553         {
554             return 0;
555         }
556 
557         int min = 0;
558         int max = nbElems - 1;
559 
560         // binary search
561         while ( min < max )
562         {
563             int middle = ( min + max + 1 ) >> 1;
564 
565             int comp = compare( keys[middle].getKey(), key );
566 
567             if ( comp < 0 )
568             {
569                 min = middle + 1;
570             }
571             else if ( comp > 0 )
572             {
573                 max = middle - 1;
574             }
575             else
576             {
577                 // Special case : the key already exists,
578                 // we can return immediately. The value will be
579                 // negative, and as the index may be 0, we subtract 1
580                 return -( middle + 1 );
581             }
582         }
583 
584         // Special case : we don't know if the key is present
585         int comp = compare( keys[max].getKey(), key );
586 
587         if ( comp == 0 )
588         {
589             return -( max + 1 );
590         }
591         else
592         {
593             if ( comp < 0 )
594             {
595                 return max + 1;
596             }
597             else
598             {
599                 return max;
600             }
601         }
602     }
603 
604 
605     /**
606      * {@inheritDoc}
607      */
608     public Tuple<K, V> findLeftMost() throws EndOfFileExceededException, IOException
609     {
610         return children[0].getValue().findLeftMost();
611     }
612 
613 
614     /**
615      * {@inheritDoc}
616      */
617     public Tuple<K, V> findRightMost() throws EndOfFileExceededException, IOException
618     {
619         return children[nbElems].getValue().findRightMost();
620     }
621 
622 
623     /**
624      * @return the btree
625      */
626     public BTree<K, V> getBtree()
627     {
628         return btree;
629     }
630 
631 
632     /**
633      * {@inheritDoc}
634      */
635     public String dumpPage( String tabs )
636     {
637         StringBuilder sb = new StringBuilder();
638 
639         if ( nbElems > 0 )
640         {
641             // Start with the first child
642             sb.append( children[0].getValue().dumpPage( tabs + "    " ) );
643 
644             for ( int i = 0; i < nbElems; i++ )
645             {
646                 sb.append( tabs );
647                 sb.append( "<" );
648                 sb.append( getKey( i ) ).append( ">\n" );
649                 sb.append( children[i + 1].getValue().dumpPage( tabs + "    " ) );
650             }
651         }
652 
653         return sb.toString();
654     }
655 
656 
657     /**
658      * @see Object#toString()
659      */
660     public String toString()
661     {
662         StringBuilder sb = new StringBuilder();
663 
664         sb.append( "r" ).append( revision );
665         sb.append( ", nbElems:" ).append( nbElems );
666 
667         if ( offset > 0 )
668         {
669             sb.append( ", offset:" ).append( offset );
670         }
671 
672         return sb.toString();
673     }
674 }