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
379
380 /**
381 * {@inheritDoc}
382 */
383 @Override
384 public V replaceValueArray( V newValue )
385 {
386 if( isSubBtree() )
387 {
388 throw new IllegalStateException( "method is not applicable for the duplicate B-Trees" );
389 }
390
391 V tmp = valueArray[0];
392
393 nbArrayElems = 1;
394 valueArray[0] = newValue;
395
396 return tmp;
397 }
398
399 }