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