/* XXL: The eXtensible and fleXible Library for data processing
Copyright (C) 2000-2011 Prof. Dr. Bernhard Seeger
Head of the Database Research Group
Department of Mathematics and Computer Science
University of Marburg
Germany
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; If not, see <http://www.gnu.org/licenses/>.
http://code.google.com/p/xxl/
*/
package xxl.core.indexStructures.vLengthBPlusTree.splitStrategy;
import java.util.Iterator;
import java.util.Stack;
import xxl.core.indexStructures.Separator;
import xxl.core.indexStructures.vLengthBPlusTree.VariableLengthBPlusTree.IndexEntry;
import xxl.core.indexStructures.vLengthBPlusTree.VariableLengthBPlusTree.Node;
import xxl.core.indexStructures.vLengthBPlusTree.VariableLengthBPlusTree.Node.SplitInfo;
/**
* Implements a shortest key strategy for the split of the index nodes and simple prefix strategy for the leaf nodes.
* It searches in the bounds [min, max] which defined by the minimal allowed byte capacity of the node for the shortest key (in bytes).
* Assumption: keys are strings.
* see
* "Prefix B-trees"
* Bayer, Rudolf and Unterauer, Karl
* ACM Trans. Database Syst. 1977
*
*/
public class SimplePrefixBPlusTreeSplit extends ShortestKeyStrategy<Object, String> {
public SimplePrefixBPlusTreeSplit() {
super();
}
@Override
public SplitInfo runSplit(Stack path, Node newNode, Node node, int min,
int max) {
// search in the range from int min to int max bytes for the smallest key
// if it index node and run shortest prefix when it is leaf node
int nodeLevel = node.getLevel();
if (nodeLevel == 0)
return leafLevelSplit(path,newNode, node, min,max);
return super.runSplit(path, newNode, node, min, max);
}
protected SplitInfo leafLevelSplit(Stack path, Node newNode, Node node, int min,
int max){
final int level = node.level();
int rightLoadBound = max;
// get Entries of the split triggered node
// get range from minLoad to average + (average-minload) or Nodesize - minload;
//filter range
int[] fromTo = this.filterFromTo(node.entries(), min, rightLoadBound, level == 0);
//search within the range
// take the first key fromTo[0] - 1 before the minLeft range
//
int splitIndex = (fromTo[0] > 0)? fromTo[0] - 1 : fromTo[0] ;
int startIndex = splitIndex ;
String prevKey = (level > 0 ) ? (String)((IndexEntry)node.getEntry(startIndex)).separator().sepValue() :
getKey(node.getEntry(startIndex));
String shortestSeparatorVal = new String(prevKey);
for (int i = startIndex+1; i < fromTo[1]; i++){
String nextKey = (level > 0) ? (String)((IndexEntry)node.getEntry(i)).separator().sepValue() :
getKey(node.getEntry(i));
// if(nextKey.startsWith("name_1430"))
// System.out.println("alarm");
String shortestPrefix = shortestPrefix(prevKey, nextKey);
if (shortestPrefix.length() < shortestSeparatorVal.length()){
splitIndex = i;
shortestSeparatorVal = shortestPrefix;
}
prevKey = nextKey;
}
// bug fix
// SplitInfo info = newNode.new SplitInfo(path, splitIndex+1);
SplitInfo info = newNode.new SplitInfo(path, splitIndex);
// create Separatoren
Separator sepNewNode = (level > 0) ? (Separator)((IndexEntry)node.getEntry(node.number()-1)).separator().clone():
createSeparator(getKey(node.getEntry(node.number()-1)));
Separator sepratorOfTriggeredNode = createSeparator(shortestSeparatorVal);
info.initialize( sepNewNode, sepratorOfTriggeredNode);
return info;
}
/**
* Assumption key1 <= key 2
* @param key1
* @param key2
* @return
*/
private String shortestPrefix(String key1, String key2){
// check
char[] keyArray1 = key1.toLowerCase().toCharArray();
char[] keyArray2 = key2.toLowerCase().toCharArray();
StringBuffer shortestString = new StringBuffer();
// iterate
for (int i = 0; i < Math.min(keyArray1.length, keyArray2.length); i++){
// check character
if (keyArray1[i] == keyArray2[i]){
shortestString.append(key1.charAt(i));
}else if (keyArray1[i] < keyArray2[i]){
shortestString.append(key2.charAt(i));
break;
}
}
return shortestString.toString();
}
/**
*
* @param entries
* @param leftRangeLoad
* @param rigthRangeLoad
* @return index array[2]
* array[0] => index of element in the range
* array[1] => bound index is not included in the range
*/
public int[] filterFromTo(Iterator entries, final int leftRangeLoad, final int rigthRangeLoad, boolean isLeafNode){
int load = 0;
int nrToLeftBound = 0;
int qualified = 0;
while(entries.hasNext()){
Object entry = entries.next();
int size = 0;
if (isLeafNode){
size = this.getObjectSize(entry);
}else{
size = this.getSeparatorSize(((IndexEntry)entry).separator()) + this.containerIdSize;
}
if (load >= leftRangeLoad ){
if ((load + size) <= rigthRangeLoad ) qualified++;
}else nrToLeftBound++;
load += size;
}
return new int[]{nrToLeftBound, qualified + nrToLeftBound};
}
}