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  
27  /**
28   * A MVCC abstract Page. It stores the field and the methods shared by the Node and Leaf
29   * classes.
30   * 
31   * @param <K> The type for the Key
32   * @param <V> The type for the stored value
33   *
34   * @author <a href="mailto:labs@labs.apache.org">Mavibot labs Project</a>
35   */
36  /* No qualifier */abstract class AbstractPage<K, V> implements Page<K, V>
37  {
38      /** Parent B+Tree. */
39      protected transient BTree<K, V> btree;
40  
41      /** This BPage's revision */
42      protected long revision;
43  
44      /** Keys of children nodes */
45      protected K[] keys;
46  
47      /** The number of current values in the Page */
48      protected int nbElems;
49  
50      /** The first {@link PageIO} storing the serialized Page on disk */
51      private long offset = -1L;
52  
53      /** The last {@link PageIO} storing the serialized Page on disk */
54      private long lastOffset = -1L;
55  
56  
57      /**
58       * Creates a default empty AbstractPage
59       * 
60       * @param btree The associated BTree
61       */
62      protected AbstractPage( BTree<K, V> btree )
63      {
64          this.btree = btree;
65      }
66  
67  
68      /**
69       * Internal constructor used to create Page instance used when a page is being copied or overflow
70       */
71      @SuppressWarnings("unchecked")
72      // Cannot create an array of generic objects
73      protected AbstractPage( BTree<K, V> btree, long revision, int nbElems )
74      {
75          this.btree = btree;
76          this.revision = revision;
77          this.nbElems = nbElems;
78  
79          // We get the type of array to create from the btree
80          // Yes, this is an hack...
81          Class<?> keyType = btree.getKeyType();
82          this.keys = ( K[] ) Array.newInstance( keyType, nbElems );
83      }
84  
85  
86      /**
87       * Selects the sibling (the previous or next page with the same parent) which has
88       * the more element assuming it's above N/2
89       * 
90       * @param parent The parent of the current page
91       * @param The position of the current page reference in its parent
92       * @return The position of the sibling, or -1 if we have'nt found any sibling
93       * @throws IOException If we have an error while trying to access the page
94       */
95      protected int selectSibling( Node<K, V> parent, int parentPos ) throws IOException
96      {
97          if ( parentPos == 0 )
98          {
99              // The current page is referenced on the left of its parent's page :
100             // we will not have a previous page with the same parent
101             return 1;
102         }
103 
104         if ( parentPos == parent.getNbElems() )
105         {
106             // The current page is referenced on the right of its parent's page :
107             // we will not have a next page with the same parent
108             return parentPos - 1;
109         }
110 
111         Page<K, V> prevPage = parent.children[parentPos - 1].getValue( btree );
112         Page<K, V> nextPage = parent.children[parentPos + 1].getValue( btree );
113 
114         int prevPageSize = prevPage.getNbElems();
115         int nextPageSize = nextPage.getNbElems();
116 
117         if ( prevPageSize >= nextPageSize )
118         {
119             return parentPos - 1;
120         }
121         else
122         {
123             return parentPos + 1;
124         }
125     }
126 
127 
128     /**
129      * {@inheritDoc}
130      */
131     public int getNbElems()
132     {
133         return nbElems;
134     }
135 
136 
137     /**
138      * Finds the position of the given key in the page. If we have found the key,
139      * we will return its position as a negative value.
140      * <p/>
141      * Assuming that the array is zero-indexed, the returned value will be : <br/>
142      *   position = - ( position + 1)
143      * <br/>
144      * So for the following table of keys : <br/>
145      * <pre>
146      * +---+---+---+---+
147      * | b | d | f | h |
148      * +---+---+---+---+
149      *   0   1   2   3
150      * </pre>
151      * looking for 'b' will return -1 (-(0+1)) and looking for 'f' will return -3 (-(2+1)).<br/>
152      * Computing the real position is just a matter to get -(position++).
153      * <p/>
154      * If we don't find the key in the table, we will return the position of the key
155      * immediately above the key we are looking for. <br/>
156      * For instance, looking for :
157      * <ul>
158      * <li>'a' will return 0</li>
159      * <li>'b' will return -1</li>
160      * <li>'c' will return 1</li>
161      * <li>'d' will return -2</li>
162      * <li>'e' will return 2</li>
163      * <li>'f' will return -3</li>
164      * <li>'g' will return 3</li>
165      * <li>'h' will return -4</li>
166      * <li>'i' will return 4</li>
167      * </ul>
168      * 
169      * 
170      * @param key The key to find
171      * @return The position in the page.
172      */
173     public int findPos( K key )
174     {
175         // Deal with the special key where we have an empty page
176         if ( nbElems == 0 )
177         {
178             return 0;
179         }
180 
181         int min = 0;
182         int max = nbElems - 1;
183 
184         // binary search
185         while ( min < max )
186         {
187             int middle = ( min + max + 1 ) >> 1;
188 
189             int comp = compare( keys[middle], key );
190 
191             if ( comp < 0 )
192             {
193                 min = middle + 1;
194             }
195             else if ( comp > 0 )
196             {
197                 max = middle - 1;
198             }
199             else
200             {
201                 // Special case : the key already exists,
202                 // we can return immediately. The value will be
203                 // negative, and as the index may be 0, we subtract 1
204                 return -( middle + 1 );
205             }
206         }
207 
208         // Special case : we don't know if the key is present
209         int comp = compare( keys[max], key );
210 
211         if ( comp == 0 )
212         {
213             return -( max + 1 );
214         }
215         else
216         {
217             if ( comp < 0 )
218             {
219                 return max + 1;
220             }
221             else
222             {
223                 return max;
224             }
225         }
226     }
227 
228 
229     /**
230      * Compares two keys
231      * 
232      * @param key1 The first key
233      * @param key2 The second key
234      * @return -1 if the first key is above the second one, 1 if it's below, and 0
235      * if the two keys are equal
236      */
237     private final int compare( K key1, K key2 )
238     {
239         if ( key1 == key2 )
240         {
241             return 0;
242         }
243 
244         if ( key1 == null )
245         {
246             return 1;
247         }
248 
249         if ( key2 == null )
250         {
251             return -1;
252         }
253 
254         return btree.getComparator().compare( key1, key2 );
255     }
256 
257 
258     /**
259      * {@inheritDoc}
260      */
261     public long getRevision()
262     {
263         return revision;
264     }
265 
266 
267     /**
268      * {@inheritDoc}
269      */
270     public K getKey( int pos )
271     {
272         if ( pos < nbElems )
273         {
274             return keys[pos];
275         }
276         else
277         {
278             return null;
279         }
280     }
281 
282 
283     /**
284      * Sets the key at a give position
285      * 
286      * @param pos The position in the keys array
287      * @param key the key to inject
288      */
289     /* No qualifier*/void setKey( int pos, K key )
290     {
291         keys[pos] = key;
292     }
293 
294 
295     /**
296      * {@inheritDoc}
297      */
298     public long getOffset()
299     {
300         return offset;
301     }
302 
303 
304     /**
305      * @param offset the offset to set
306      */
307     /* No qualifier */void setOffset( long offset )
308     {
309         this.offset = offset;
310     }
311 
312 
313     /**
314      * {@inheritDoc}
315      */
316     public long getLastOffset()
317     {
318         return lastOffset;
319     }
320 
321 
322     /**
323      * {@inheritDoc}
324      */
325     /* No qualifier */void setLastOffset( long lastOffset )
326     {
327         this.lastOffset = lastOffset;
328     }
329 
330 
331     /**
332      * @see Object#toString()
333      */
334     public String toString()
335     {
336         StringBuilder sb = new StringBuilder();
337 
338         sb.append( "r" ).append( revision );
339         sb.append( ", nbElems:" ).append( nbElems );
340 
341         if ( offset > 0 )
342         {
343             sb.append( ", offset:" ).append( offset );
344         }
345 
346         return sb.toString();
347     }
348 
349 
350     /**
351      * {@inheritDoc}
352      */
353     public String dumpPage( String tabs )
354     {
355         return "";
356     }
357 }