| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| */ |
| package org.apache.directory.mavibot.btree; |
| |
| |
| import java.io.IOException; |
| import java.lang.reflect.Array; |
| import java.util.Comparator; |
| |
| import org.apache.directory.mavibot.btree.exception.KeyNotFoundException; |
| import org.apache.directory.mavibot.btree.serializer.ElementSerializer; |
| |
| |
| /** |
| * A holder to store the Values |
| * |
| * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> |
| * @param <V> The value type |
| */ |
| /* No qualifier*/abstract class AbstractValueHolder<V> implements ValueHolder<V> |
| { |
| /** The BTree storing multiple value, if we have more than one value */ |
| protected BTree<V, V> valueBtree; |
| |
| /** The array storing from 1 to N values */ |
| protected V[] valueArray; |
| |
| /** The Value serializer */ |
| protected ElementSerializer<V> valueSerializer; |
| |
| /** The configuration for the array <-> BTree switch. Default to 1 */ |
| protected int valueThresholdUp = 1; |
| protected int valueThresholdLow = 1; |
| |
| protected int nbArrayElems; |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public boolean isSubBtree() |
| { |
| return valueBtree != null; |
| } |
| |
| |
| /** |
| * Create a clone of this instance |
| */ |
| public ValueHolder<V> clone() throws CloneNotSupportedException |
| { |
| ValueHolder<V> copy = ( ValueHolder<V> ) super.clone(); |
| |
| return copy; |
| } |
| |
| |
| /** |
| * @return a cursor on top of the values |
| */ |
| public ValueCursor<V> getCursor() |
| { |
| if ( valueBtree != null ) |
| { |
| return new ValueBTreeCursor<V>( valueBtree ); |
| } |
| else |
| { |
| return new ValueArrayCursor<V>( valueArray ); |
| } |
| } |
| |
| |
| /** |
| * Find the position of a given value in the array, or the position where we |
| * would insert the element (in this case, the position will be negative). |
| * As we use a 0-based array, the negative position for 0 is -1. |
| * -1 means the element can be added in position 0 |
| * -2 means the element can be added in position 1 |
| * ... |
| */ |
| private int findPos( V value ) |
| { |
| if ( valueArray.length == 0 ) |
| { |
| return -1; |
| } |
| |
| // Do a search using dichotomy |
| int pivot = valueArray.length / 2; |
| int low = 0; |
| int high = valueArray.length - 1; |
| Comparator<V> comparator = valueSerializer.getComparator(); |
| |
| while ( high > low ) |
| { |
| switch ( high - low ) |
| { |
| case 1: |
| // We have 2 elements |
| int result = comparator.compare( value, valueArray[pivot] ); |
| |
| if ( result == 0 ) |
| { |
| return pivot; |
| } |
| |
| if ( result < 0 ) |
| { |
| if ( pivot == low ) |
| { |
| return -( low + 1 ); |
| } |
| else |
| { |
| result = comparator.compare( value, valueArray[low] ); |
| |
| if ( result == 0 ) |
| { |
| return low; |
| } |
| |
| if ( result < 0 ) |
| { |
| return -( low + 1 ); |
| } |
| else |
| { |
| return -( low + 2 ); |
| } |
| } |
| } |
| else |
| { |
| if ( pivot == high ) |
| { |
| return -( high + 2 ); |
| } |
| else |
| { |
| result = comparator.compare( value, valueArray[high] ); |
| |
| if ( result == 0 ) |
| { |
| return high; |
| } |
| |
| if ( result < 0 ) |
| { |
| return -( high + 1 ); |
| } |
| else |
| { |
| return -( high + 2 ); |
| } |
| } |
| } |
| |
| default: |
| // We have 3 elements |
| result = comparator.compare( value, valueArray[pivot] ); |
| |
| if ( result == 0 ) |
| { |
| return pivot; |
| } |
| |
| if ( result < 0 ) |
| { |
| high = pivot - 1; |
| } |
| else |
| { |
| low = pivot + 1; |
| } |
| |
| pivot = ( high + low ) / 2; |
| |
| continue; |
| } |
| } |
| |
| int result = comparator.compare( value, valueArray[pivot] ); |
| |
| if ( result == 0 ) |
| { |
| return pivot; |
| } |
| |
| if ( result < 0 ) |
| { |
| return -( pivot + 1 ); |
| } |
| else |
| { |
| return -( pivot + 2 ); |
| } |
| } |
| |
| |
| /** |
| * Check if the array of values contains a given value |
| */ |
| private boolean arrayContains( V value ) |
| { |
| if ( valueArray.length == 0 ) |
| { |
| return false; |
| } |
| |
| // Do a search using dichotomy |
| return findPos( value ) >= 0; |
| } |
| |
| |
| /** |
| * Check if the subBtree contains a given value |
| */ |
| protected boolean btreeContains( V value ) |
| { |
| try |
| { |
| return valueBtree.hasKey( value ); |
| } |
| catch ( IOException e ) |
| { |
| // TODO Auto-generated catch block |
| e.printStackTrace(); |
| return false; |
| } |
| catch ( KeyNotFoundException knfe ) |
| { |
| knfe.printStackTrace();return false; |
| } |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public boolean contains( V checkedValue ) |
| { |
| if ( valueArray == null ) |
| { |
| return btreeContains( checkedValue ); |
| } |
| else |
| { |
| return arrayContains( checkedValue ); |
| } |
| } |
| |
| |
| /** |
| * Create a new Sub-BTree to store the values. |
| */ |
| protected abstract void createSubTree(); |
| |
| |
| /** |
| * Add the value in an array |
| */ |
| private void addInArray( V value ) |
| { |
| // We have to check that we have reached the threshold or not |
| if ( size() >= valueThresholdUp ) |
| { |
| // Ok, transform the array into a btree |
| createSubTree(); |
| |
| try |
| { |
| for ( V val : valueArray ) |
| { |
| // Here, we should insert all the values in one shot then |
| // write the btree on disk only once. |
| valueBtree.insert( val, null ); |
| } |
| |
| // We can delete the array now |
| nbArrayElems = 0; |
| valueArray = null; |
| |
| // And inject the new value |
| valueBtree.insert( value, null ); |
| } |
| catch ( IOException e ) |
| { |
| // TODO Auto-generated catch block |
| e.printStackTrace(); |
| } |
| } |
| else |
| { |
| // Create the array if it's null |
| if ( valueArray == null ) |
| { |
| valueArray = ( V[] ) Array.newInstance( valueSerializer.getType(), 1 ); |
| nbArrayElems = 1; |
| valueArray[0] = value; |
| } |
| else |
| { |
| // check that the value is not already present in the ValueHolder |
| int pos = findPos( value ); |
| |
| if ( pos >= 0 ) |
| { |
| // The value exists : nothing to do |
| return; |
| } |
| |
| // Ok, we just have to insert the new element at the right position |
| // We transform the position to a positive value |
| pos = -( pos + 1 ); |
| // First, copy the array |
| V[] newValueArray = ( V[] ) Array.newInstance( valueSerializer.getType(), valueArray.length + 1 ); |
| |
| System.arraycopy( valueArray, 0, newValueArray, 0, pos ); |
| newValueArray[pos] = value; |
| System.arraycopy( valueArray, pos, newValueArray, pos + 1, valueArray.length - pos ); |
| |
| // And switch the arrays |
| valueArray = newValueArray; |
| } |
| } |
| } |
| |
| |
| /** |
| * Add the value in the subBTree |
| */ |
| private void addInBtree( V value ) |
| { |
| try |
| { |
| valueBtree.insert( value, null ); |
| } |
| catch ( IOException e ) |
| { |
| // TODO Auto-generated catch block |
| e.printStackTrace(); |
| } |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public void add( V value ) |
| { |
| if ( valueBtree == null ) |
| { |
| addInArray( value ); |
| } |
| else |
| { |
| addInBtree( value ); |
| } |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public V replaceValueArray( V newValue ) |
| { |
| if( isSubBtree() ) |
| { |
| throw new IllegalStateException( "method is not applicable for the duplicate B-Trees" ); |
| } |
| |
| V tmp = valueArray[0]; |
| |
| nbArrayElems = 1; |
| valueArray[0] = newValue; |
| |
| return tmp; |
| } |
| |
| } |