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  import java.util.Comparator;
26  
27  import org.apache.directory.mavibot.btree.serializer.ElementSerializer;
28  
29  
30  /**
31   * A holder to store the Values
32   * 
33   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
34   * @param <V> The value type
35   */
36  /* No qualifier*/abstract class AbstractValueHolder<V> implements ValueHolder<V>
37  {
38      /** The BTree storing multiple value, if we have more than one value */
39      protected BTree<V, V> valueBtree;
40  
41      /** The array storing from 1 to N values */
42      protected V[] valueArray;
43  
44      /** The Value serializer */
45      protected ElementSerializer<V> valueSerializer;
46  
47      /** The configuration for the array <-> BTree switch. Default to 1 */
48      protected int valueThresholdUp = 1;
49      protected int valueThresholdLow = 1;
50  
51      protected int nbArrayElems;
52  
53  
54      /**
55       * {@inheritDoc}
56       */
57      public boolean isSubBtree()
58      {
59          return valueBtree != null;
60      }
61  
62  
63      /**
64       * Create a clone of this instance
65       */
66      public ValueHolder<V> clone() throws CloneNotSupportedException
67      {
68          ValueHolder<V> copy = ( ValueHolder<V> ) super.clone();
69  
70          return copy;
71      }
72  
73  
74      /**
75       * @return a cursor on top of the values
76       */
77      public ValueCursor<V> getCursor()
78      {
79          if ( valueBtree != null )
80          {
81              return new ValueBTreeCursor<V>( valueBtree );
82          }
83          else
84          {
85              return new ValueArrayCursor<V>( valueArray );
86          }
87      }
88  
89  
90      /**
91       * Find the position of a given value in the array, or the position where we
92       * would insert the element (in this case, the position will be negative).
93       * As we use a 0-based array, the negative position for 0 is -1.
94       * -1 means the element can be added in position 0
95       * -2 means the element can be added in position 1
96       * ... 
97       */
98      private int findPos( V value )
99      {
100         if ( valueArray.length == 0 )
101         {
102             return -1;
103         }
104 
105         // Do a search using dichotomy
106         int pivot = valueArray.length / 2;
107         int low = 0;
108         int high = valueArray.length - 1;
109         Comparator<V> comparator = valueSerializer.getComparator();
110 
111         while ( high > low )
112         {
113             switch ( high - low )
114             {
115                 case 1:
116                     // We have 2 elements
117                     int result = comparator.compare( value, valueArray[pivot] );
118 
119                     if ( result == 0 )
120                     {
121                         return pivot;
122                     }
123 
124                     if ( result < 0 )
125                     {
126                         if ( pivot == low )
127                         {
128                             return -( low + 1 );
129                         }
130                         else
131                         {
132                             result = comparator.compare( value, valueArray[low] );
133 
134                             if ( result == 0 )
135                             {
136                                 return low;
137                             }
138 
139                             if ( result < 0 )
140                             {
141                                 return -( low + 1 );
142                             }
143                             else
144                             {
145                                 return -( low + 2 );
146                             }
147                         }
148                     }
149                     else
150                     {
151                         if ( pivot == high )
152                         {
153                             return -( high + 2 );
154                         }
155                         else
156                         {
157                             result = comparator.compare( value, valueArray[high] );
158 
159                             if ( result == 0 )
160                             {
161                                 return high;
162                             }
163 
164                             if ( result < 0 )
165                             {
166                                 return -( high + 1 );
167                             }
168                             else
169                             {
170                                 return -( high + 2 );
171                             }
172                         }
173                     }
174 
175                 default:
176                     // We have 3 elements
177                     result = comparator.compare( value, valueArray[pivot] );
178 
179                     if ( result == 0 )
180                     {
181                         return pivot;
182                     }
183 
184                     if ( result < 0 )
185                     {
186                         high = pivot - 1;
187                     }
188                     else
189                     {
190                         low = pivot + 1;
191                     }
192 
193                     pivot = ( high + low ) / 2;
194 
195                     continue;
196             }
197         }
198 
199         int result = comparator.compare( value, valueArray[pivot] );
200 
201         if ( result == 0 )
202         {
203             return pivot;
204         }
205 
206         if ( result < 0 )
207         {
208             return -( pivot + 1 );
209         }
210         else
211         {
212             return -( pivot + 2 );
213         }
214     }
215 
216 
217     /**
218      * Check if the array of values contains a given value
219      */
220     private boolean arrayContains( V value )
221     {
222         if ( valueArray.length == 0 )
223         {
224             return false;
225         }
226 
227         // Do a search using dichotomy
228         return findPos( value ) >= 0;
229     }
230 
231 
232     /**
233      * Check if the subBtree contains a given value
234      */
235     protected boolean btreeContains( V value )
236     {
237         try
238         {
239             return valueBtree.hasKey( value );
240         }
241         catch ( IOException e )
242         {
243             // TODO Auto-generated catch block
244             e.printStackTrace();
245             return false;
246         }
247     }
248 
249 
250     /**
251      * {@inheritDoc}
252      */
253     public boolean contains( V checkedValue )
254     {
255         if ( valueArray == null )
256         {
257             return btreeContains( checkedValue );
258         }
259         else
260         {
261             return arrayContains( checkedValue );
262         }
263     }
264 
265 
266     /**
267      * Create a new Sub-BTree to store the values.
268      */
269     protected abstract void createSubTree();
270 
271 
272     /**
273      * Add the value in an array
274      */
275     private void addInArray( V value )
276     {
277         // We have to check that we have reached the threshold or not
278         if ( size() >= valueThresholdUp )
279         {
280             // Ok, transform the array into a btree
281             createSubTree();
282 
283             try
284             {
285                 for ( V val : valueArray )
286                 {
287                     // Here, we should insert all the values in one shot then 
288                     // write the btree on disk only once.
289                     valueBtree.insert( val, null );
290                 }
291 
292                 // We can delete the array now
293                 nbArrayElems = 0;
294                 valueArray = null;
295 
296                 // And inject the new value
297                 valueBtree.insert( value, null );
298             }
299             catch ( IOException e )
300             {
301                 // TODO Auto-generated catch block
302                 e.printStackTrace();
303             }
304         }
305         else
306         {
307             // Create the array if it's null
308             if ( valueArray == null )
309             {
310                 valueArray = ( V[] ) Array.newInstance( valueSerializer.getType(), 1 );
311                 nbArrayElems = 1;
312                 valueArray[0] = value;
313             }
314             else
315             {
316                 // check that the value is not already present in the ValueHolder
317                 int pos = findPos( value );
318 
319                 if ( pos >= 0 )
320                 {
321                     // The value exists : nothing to do
322                     return;
323                 }
324 
325                 // Ok, we just have to insert the new element at the right position
326                 // We transform the position to a positive value 
327                 pos = -( pos + 1 );
328                 // First, copy the array
329                 V[] newValueArray = ( V[] ) Array.newInstance( valueSerializer.getType(), valueArray.length + 1 );
330 
331                 System.arraycopy( valueArray, 0, newValueArray, 0, pos );
332                 newValueArray[pos] = value;
333                 System.arraycopy( valueArray, pos, newValueArray, pos + 1, valueArray.length - pos );
334 
335                 // And switch the arrays
336                 valueArray = newValueArray;
337             }
338         }
339     }
340 
341 
342     /**
343      * Add the value in the subBTree
344      */
345     private void addInBtree( V value )
346     {
347         try
348         {
349             valueBtree.insert( value, null );
350         }
351         catch ( IOException e )
352         {
353             // TODO Auto-generated catch block
354             e.printStackTrace();
355         }
356     }
357 
358 
359     /**
360      * {@inheritDoc}
361      */
362     public void add( V value )
363     {
364         if ( valueBtree == null )
365         {
366             addInArray( value );
367         }
368         else
369         {
370             addInBtree( value );
371         }
372     }
373 }