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