/* 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;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Stack;
import java.util.Map.Entry;
import xxl.core.collections.MapEntry;
import xxl.core.collections.MappedList;
import xxl.core.collections.containers.Container;
import xxl.core.cursors.Cursor;
import xxl.core.cursors.filters.Filter;
import xxl.core.cursors.unions.Merger;
import xxl.core.cursors.unions.Sequentializer;
import xxl.core.functions.AbstractFunction;
import xxl.core.functions.Function;
import xxl.core.indexStructures.BPlusTree.IndexEntry;
import xxl.core.indexStructures.BPlusTree.KeyRange;
import xxl.core.indexStructures.BPlusTree.Node.SplitInfo;
import xxl.core.indexStructures.MVBTree.LeafEntry;
import xxl.core.indexStructures.MVBTree.Lifespan;
import xxl.core.indexStructures.MVBTree.MVRegion;
import xxl.core.indexStructures.MVBTree.MVSeparator;
import xxl.core.indexStructures.MVBTree.Node;
import xxl.core.indexStructures.MVBTree.Root;
import xxl.core.indexStructures.MVBTree.Version;
import xxl.core.io.converters.BooleanConverter;
import xxl.core.io.converters.IntegerConverter;
import xxl.core.io.converters.MeasuredConverter;
import xxl.core.predicates.AbstractPredicate;
import xxl.core.predicates.Predicate;
/**
* MVBTree wiht new node layout
*
*/
public class MVBT extends MVBTree {
/**
* Comparator for live entries
*/
protected Comparator<Object> liveIndexEntryComparator = new Comparator<Object>() {
@SuppressWarnings("unchecked")
@Override
public int compare(Object arg0, Object arg1) {
MVSeparator mv1 =(MVSeparator)((IndexEntry)arg0).separator;
MVSeparator mv2 = (MVSeparator)((IndexEntry)arg1).separator;
return mv1.sepValue().compareTo(mv2.sepValue());
}
};
/**Creates a new <tt>MVBTree</tt>.
* KeyDomainMinValue set to default value @see {@link MVBT#DEAFUALT_KEYDOMAIN_MINVALUE}
* @param blockSize the block size of the underlaying <tt>Container</tt>.
* @param minCapRatio the minimal capacity ratio of the tree's nodes.
* @param e the epsilon of the <tt>strong version condition</tt>.
*/
public MVBT(int blockSize, float minCapRatio, float e) {
super(blockSize, minCapRatio,e);
}
/**Creates a new <tt>MVBTree</tt>. The minimal capacity ratio of the tree's nodes is set ot 50%.
* @param blockSize the block size of the underlaying <tt>Container</tt>.
* @param e the epsilon of the <tt>strong version condition</tt>.
*/
public MVBT(int blockSize, float e) {
this(blockSize, 0.5f, e);
}
/**Creates a new <tt>MVBTree</tt>. The minimal capacity ratio of the tree's nodes is set ot 50%.
* @param blockSize the block size of the underlaying <tt>Container</tt>.
* @param e the epsilon of the <tt>strong version condition</tt>.
*/
public MVBT(int blockSize, float e, Comparable keyDomainMinValue) {
this(blockSize, 0.5f, e, keyDomainMinValue);
}
public MVBT(int blockSize, float minCapRatio, float e, Comparable keyDomainMinValue ) {
super(blockSize, minCapRatio, e, keyDomainMinValue);
}
/**
*
* @param rootEntry
* @param rootsRootEntry
* @param getKey
* @param rootsContainer
* @param treeContainer
* @param versionConverter
* @param keyConverter
* @param dataConverter
* @param createMVSeparator
* @param createMVRegion
* @return
*/
public MVBT initialize(IndexEntry rootEntry,
Descriptor liveRootDescriptor,
IndexEntry rootsRootEntry,
Descriptor rootsRootDescriptor,
final Function getKey,
final Container rootsContainer,
final Container treeContainer,
MeasuredConverter versionConverter,
MeasuredConverter keyConverter,
MeasuredConverter dataConverter,
Function createMVSeparator,
Function createMVRegion){
super.initialize(rootEntry,
liveRootDescriptor,
rootsRootEntry,
rootsRootDescriptor,
getKey,
rootsContainer,
treeContainer,
versionConverter,
keyConverter,
dataConverter,
createMVSeparator,
createMVRegion);
return this;
}
/** Creates a new <tt>Node</tt>.
* @return the new created <tt>Node</tt>.
*/
public Tree.Node createNode(int level) {
return new Node(level);
}
/** Creates a new <tt>NodeConverter</tt>.
* @return the new created <tt>NodeConverter</tt>.
*/
protected BPlusTree.NodeConverter createNodeConverter() {
return new NodeConverter();
}
/**
* Builds a path from the appropriate root node to the node which
* contains the given key in the given version.
*
* @param key key to query for.
* @param version version to query for.
* @param level tree level at which the search should stop.
* @return path from a root node to the correct node at the given level.
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
protected Stack pathToNodeLiveRemove(Object key, Version version, int level) {
IndexEntry indexEntry = determineRootEntry(version);
if (indexEntry == null) throw new IllegalStateException();
Stack path = new Stack();
down(path, indexEntry);
Node node = (Node)node(path);
MVSeparator sep = createMVSeparator( version,version, (Comparable)key);
while (node.level() > level) {
indexEntry = null;
indexEntry = (IndexEntry) node.chooseSubtree(sep);
if (indexEntry == null)
throw new IllegalStateException();
down(path, indexEntry);
node = (Node)node(path);
}
return path;
}
/*
* (non-Javadoc)
* @see xxl.core.indexStructures.MVBTree#remove(xxl.core.indexStructures.MVBTree.Version, java.lang.Object)
*/
@SuppressWarnings({ "unchecked", "rawtypes", "deprecation" })
public Object remove(Version removeVersion, Object data) {
setCurrentVersion(removeVersion);
Object key = getKey.invoke(data);
Stack path = pathToNodeLiveRemove(key, currentVersion(), 0);
Iterator it = ((MVBTree.Node)node(path)).iterator();
LeafEntry removed = null;
while (it.hasNext()) {
LeafEntry obj = (LeafEntry)it.next();
if (obj.data().equals(data) && obj.getLifespan().isAlive()) {
it.remove();
removed = obj;
break;
}
}
treatUnderflow(path);
return removed;
}
/*
* (non-Javadoc)
* @see xxl.core.indexStructures.MVBTree#update(xxl.core.indexStructures.MVBTree.Version, java.lang.Object, java.lang.Object)
*/
public void update (Version updateVersion, Object oldData, Object newData) {
setCurrentVersion(updateVersion);
Object key = getKey.invoke(newData);
Stack path = pathToNodeLiveRemove(key, currentVersion(), 0);
Iterator it = ((MVBTree.Node)node(path)).iterator();
LeafEntry removed = null;
while (it.hasNext()) {
LeafEntry obj = (LeafEntry)it.next();
if (obj.data().equals(oldData) && obj.getLifespan().isAlive()) {
it.remove();
removed = obj;
Lifespan lifespan=new Lifespan(currentVersion());
LeafEntry newEntry= new LeafEntry(lifespan, newData);
node(path).grow(newEntry, path);
break;
}
}
treatUnderflow(path);
}
/**
* The experimantal layout: for index nodes we manage two lists
* first list stores live entries
* second list stores deleted entries
*
* this allows to run chooseSubTree method in O(log B) instead of O(B)
* and we have only 4 bytes space overhead
* @author Daniar
*/
public class Node extends MVBTree.Node {
/**
* experimetal part
*/
public List liveEntries;
/***********************************************************
*
* new code live entries
*
***********************************************************/
@Override
public int number() {
return super.number() + liveEntries.size();
}
/**
*
* @param entry
*/
public void insertIntoLiveList(IndexEntry entry){
int index = searchInLiveList(entry);
if(index >= 0 ){
throw new RuntimeException("Insertion failed entry with same key in live list found!");
}
index=-index-1;
liveEntries.add(index, entry);
}
/**
*
* @param separator
* @return
*/
public int searchInLiveList(MVSeparator separator){
IndexEntry idx = new IndexEntry(level);
idx.initialize(separator);
return searchInLiveList(idx);
}
/**
*
* @param idx
* @return
*/
public int searchInLiveList(IndexEntry idx){
int index = Collections.binarySearch(liveEntries, idx, liveIndexEntryComparator);
return index;
}
/**
*
* @param entry
*/
public void removeFromLiveList(IndexEntry entry){
int index = searchInLiveList(entry);
if(index<0){
throw new RuntimeException("Entry not found ");
}
liveEntries.remove(index);
}
/**
*
* @param entry
*/
@SuppressWarnings("rawtypes")
public void removeFromLiveListAddToOldList(IndexEntry entry){
int index = searchInLiveList(entry);
if(index<0){
throw new RuntimeException("Entry not found ");
}
liveEntries.remove(index);
this.grow(entry, new Stack());
}
/**
*
* @param separator
* @return
*/
public IndexEntry chooseSubTreeFromLiveList(MVSeparator separator){
int index = searchInLiveList(separator);
if(index >= 0){
return (IndexEntry) liveEntries.get(index);
}
index = -index-1;
if(index == 0){
return (IndexEntry) liveEntries.get(index);
}
index -=1;
return (IndexEntry) liveEntries.get(index);
}
/***********************************************************
*
* new code live entries
*
***********************************************************/
/**Creates a new <tt>Node</tt>.
* @param level the level of the <tt>Node</tt>.
* @param createEntryList a Function to create the entry list.
*/
public Node(int level, Function createEntryList) {
super(level, createEntryList);
}
/**Creates a new <tt>Node</tt>.
* @param level the level of the <tt>Node</tt>.
*/
public Node(int level) {
super(level);
liveEntries = new ArrayList<>();
}
/*
* (non-Javadoc)
* @see xxl.core.indexStructures.BPlusTree.Node#initialize(java.lang.Object)
*/
protected Tree.Node.SplitInfo initialize(Object entry) {
Stack path = new Stack();
grow(entry, path);
if(level > 0){
return new SplitInfo(path).initialize((Separator)separator(this.liveEntries.get(this.liveEntries.size()-1)).clone());
}
return new SplitInfo(path).initialize((Separator)separator(this.getLast()).clone());
}
/*
* (non-Javadoc)
* @see xxl.core.indexStructures.MVBTree.Node#redressOverflow(java.util.Stack, java.util.List, boolean)
*/
@SuppressWarnings("rawtypes")
protected Collection redressOverflow (Stack path, List newIndexEntries, boolean up) {
if (overflows() || underflows()) {
if((path.size()==1)&& underflows() && !overflows()) {
// root
Node rootNode=(Node)node(path);
if((rootNode.level()!=0)&&(rootNode.countCurrentEntries()==1)) {
reorg=true;
Object oldRootId= rootEntry.id();
int rootParentLevel=rootEntry.parentLevel();
MVRegion oldRootReg= toMVRegion((MVSeparator)((IndexEntry)rootEntry).separator());
Version delVersion=oldRootReg.beginVersion();
Iterator iter= rootNode.entries();
while(iter.hasNext()) {
MVSeparator mvSep=(MVSeparator)((IndexEntry)iter.next()).separator;
if(mvSep.isDead() && mvSep.deleteVersion().compareTo(delVersion)>0)
delVersion=mvSep.deleteVersion();
}
Iterator current=rootNode.getCurrentEntries();
if(current.hasNext()) rootEntry=(IndexEntry)current.next();
else throw new IllegalStateException();
//oldRootReg.updateMaxBound(((MVRegion)rootDescriptor).maxBound());
oldRootReg.updateEndVersion(delVersion);
Root oldRoot= new Root(oldRootReg, oldRootId, rootParentLevel);
// new code
// if multiple operations are allowed in one version, a root with an
// empty lifespan may be generated. It has to be discarded.
if (!oldRootReg.lifespan().isPoint()) {
roots.insert(oldRoot);
//Daniar: new code
KeyRange newRange = roots.createKeyRange(oldRootReg.beginVersion(), currentVersion);
roots.rootDescriptor.union(newRange);
// ((Lifespan)roots.rootDescriptor).updateMaxBound(currentVersion);
// .updateMaxBound(currentVersion);
}
}
}
else {
reorg=true;
Node versionSplitNode=(Node)MVBT.this.createNode(level());
SplitInfo splitInfo= (SplitInfo)versionSplitNode.split(path);
IndexEntry indexEntryOfVersionSplitNode=(IndexEntry) createIndexEntry(level()+1);
Object idOfVersionSplitNode=
indexEntryOfVersionSplitNode.container().insert(splitInfo.versionSplitNode());
splitInfo.versionSplitNode().onInsert(idOfVersionSplitNode);
indexEntryOfVersionSplitNode.initialize(idOfVersionSplitNode,
splitInfo.getMVSeparatorOfVersionSplitNode());
IndexEntry indexEntryOfKeySplitNode=null;
// Test
if(splitInfo.isKeySplit()) {
indexEntryOfKeySplitNode=(IndexEntry)createIndexEntry(indexEntryOfVersionSplitNode.parentLevel());
Object idOfKeySplitNode= indexEntryOfKeySplitNode.container().insert(splitInfo.keySplitNode);
splitInfo.keySplitNode.onInsert(idOfKeySplitNode);
indexEntryOfKeySplitNode.initialize(idOfKeySplitNode,
splitInfo.getMVSeparatorOfKeySplitNode());
}
//A root overflow...
if (splitInfo.isRootSplit()) {
int oldRootParentLevel= rootEntry.parentLevel();
Object oldRootId= rootEntry.id();
MVRegion oldRootReg= toMVRegion((MVSeparator)((IndexEntry)rootEntry).separator());
if(splitInfo.isKeySplit()) {
Entry newRootTuple = MVBT.this.grow(indexEntryOfVersionSplitNode);
Node newRootNode= (Node)newRootTuple.getValue();
newRootNode.grow(indexEntryOfKeySplitNode, new Stack());
MVSeparator rootSeparator= (MVSeparator)indexEntryOfVersionSplitNode.separator;
Object newRootNodeId= MVBT.this.container().insert(newRootNode);
newRootNode.onInsert(newRootNodeId);
((IndexEntry)rootEntry).initialize(newRootNodeId,rootSeparator);
}
else rootEntry = indexEntryOfVersionSplitNode;
oldRootReg.updateMaxBound(((MVRegion)rootDescriptor).maxBound());
Root oldRoot= new Root(oldRootReg, oldRootId, oldRootParentLevel);
// if multiple operations are allowed in one version, a root with an
// empty lifespan may be generated. It has to be discarded.
if (!oldRootReg.lifespan().isPoint()) {
roots.insert(oldRoot);
//Daniar: new code bug fix
KeyRange newRange = roots.createKeyRange(oldRootReg.beginVersion(), currentVersion);
roots.rootDescriptor.union(newRange);
// ((Lifespan)roots.rootDescriptor).updateMaxBound(currentVersion);
}
}
else {
MapEntry pathEntry = (MapEntry)path.pop();
Node parentNode = (Node)node(path);
// new code first grow
parentNode.grow(indexEntryOfVersionSplitNode, path);
newIndexEntries.add(newIndexEntries.size(), indexEntryOfVersionSplitNode);
if(splitInfo.isKeySplit()) {
parentNode.grow(indexEntryOfKeySplitNode, path);
newIndexEntries.add(newIndexEntries.size(), indexEntryOfKeySplitNode);
}
parentNode.sortEntries();
// new code first grow
path.push(pathEntry);
}
}
}
if (up) {
update(path);
up(path);
}
if (level==0){
counter+=newIndexEntries.size();
leafentries.addAll(newIndexEntries);
}
return newIndexEntries;
}
/*
* (non-Javadoc)
* @see xxl.core.indexStructures.MVBTree.Node#chooseSubtree(xxl.core.indexStructures.Descriptor, java.util.Stack)
*/
@SuppressWarnings("rawtypes")
protected Tree.IndexEntry chooseSubtree (Descriptor descriptor, Stack path) {
final MVSeparator mvSeparator= (MVSeparator) descriptor;
return chooseSubTreeFromLiveList(mvSeparator);
}
/*
* (non-Javadoc)
* @see xxl.core.indexStructures.MVBTree.Node#chooseSubtree(xxl.core.indexStructures.MVBTree.MVSeparator)
*/
public Tree.IndexEntry chooseSubtree(MVSeparator mvSeparator){
return chooseSubTreeFromLiveList(mvSeparator);
}
/*
* (non-Javadoc)
* @see xxl.core.indexStructures.MVBTree.Node#referenceLeafQuery(xxl.core.indexStructures.MVBTree.MVRegion, xxl.core.indexStructures.MVBTree.MVRegion)
*/
@SuppressWarnings("rawtypes")
protected Cursor referenceLeafQuery(final MVRegion queryRegion, final MVRegion nodeRegion) {
if(level()>0) throw new UnsupportedOperationException("The node is not a leaf.");
Predicate test=new AbstractPredicate() {
public boolean invoke(Object entry) {//Umgestellt auf halboffene Intervale
MVSeparator entrySeparator = (MVSeparator)separator(entry);
Lifespan entryLifeSpan = entrySeparator.lifespan;
Comparable entryKey = entrySeparator.sepValue;
if (queryRegion.contains(entryKey) && queryRegion.lifespan.overlaps(entryLifeSpan) ){
Comparable referenceKey= max(queryRegion.minBound(), entryKey);
Version referenceTime=(Version) min(queryRegion.endVersion(), entryLifeSpan.endVersion());
if (nodeRegion.contains(referenceKey) ){
if (nodeRegion.isAlive()) return true;
else if ((nodeRegion.endVersion().compareTo(referenceTime)>0)){
return true;
}//sonder fall EntryIntervall endet in rechte Grenze von NodeIntervall
else if ((entryLifeSpan.isDead())
&& entryLifeSpan.endVersion().compareTo(nodeRegion.endVersion()) == 0){
return true;
}
}
}
return false;
}
};
return new Filter(new Sequentializer(this.iterator(), liveEntries.iterator()), test);
}
/*
* (non-Javadoc)
* @see xxl.core.indexStructures.MVBTree.Node#coordinatesOf(xxl.core.indexStructures.MVBTree.Version)
*/
protected int[] coordinatesOf(Version version) {
int from=search(version);
int to=from;
if(from>=0) {
to=from+1;
while(to<entries.size()){
if(((MVSeparator)separator(getEntry(to))).insertVersion().compareTo(version)>0) break;
to++;
}
}
return new int[]{from,to};
}
/**
* Gives the entry stored on the given position in the <tt>Node</tt>.
*
* @param index
* the position of the required entry
* @return the entry stored on the given position in the <tt>Node</tt>
* or <tt>null</tt> if the <tt>Node</tt> is empty
*/
public Object getEntry(int index) {
if (entries.isEmpty() && liveEntries.isEmpty()) return null;
if (index < entries.size())
return entries.get(index);
int nIndex = index - entries.size();
return liveEntries.get(nIndex);
}
/*
* (non-Javadoc)
* @see xxl.core.indexStructures.MVBTree.Node#query(xxl.core.indexStructures.MVBTree.Lifespan)
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public Iterator query(final Lifespan lifespan) {
return new Filter( new Sequentializer(iterator(), liveEntries.iterator()),
new AbstractPredicate() {
public boolean invoke(Object entry) {
return ((MVSeparator)separator(entry)).lifespan().overlaps(lifespan);
}
});
}
/*
* (non-Javadoc)
* @see xxl.core.indexStructures.MVBTree.Node#iterator()
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public Iterator iterator() {
if(level > 0 )
return new Sequentializer(entries.iterator(), liveEntries.iterator());
return new Iterator() {
private int index=0;
private boolean removeable=false;
public boolean hasNext() {
return index<MVBT.Node.this.number();
}
public Object next() {
if(!hasNext()) throw new NoSuchElementException();
removeable=true;
return MVBT.Node.this.getEntry(index++);
}
public void remove() {
if(removeable) {
removeable=false;
MVBT.Node.this.remove(index-1);
}
else throw new NoSuchElementException();
}
};
}
/*
* (non-Javadoc)
* @see xxl.core.indexStructures.MVBTree.Node#getCurrentEntries()
*/
@SuppressWarnings({ "unchecked", "rawtypes", "serial" })
public Iterator getCurrentEntries() {
return new Filter( new Sequentializer(iterator(), liveEntries.iterator()),
new AbstractPredicate() {
public boolean invoke(Object entry) {
return ((MVSeparator)separator(entry)).isAlive();
}
});
}
/*
* (non-Javadoc)
* @see xxl.core.indexStructures.MVBTree.Node#grow(java.lang.Object, java.util.Stack)
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
protected void grow(Object entry, Stack path) {
// experimental code
if(level > 0 && ((MVSeparator)((IndexEntry)entry).separator()).isAlive()){
insertIntoLiveList((IndexEntry)entry);
}else{
if (level() != 0)
removePointEntries();
int index;
if(number()==0) index=-1;
else {
MVSeparator mvSep=(MVSeparator)separator(entry);
int[] coord= coordinatesOf(mvSep.insertVersion());
index= coord[0];
if(index>=0) {
index= search(mvSep.sepValue(), coord[0], coord[1]);
}
}
if(index>=0) {
throw new RuntimeException("Insertion failed: An entry having the same key was found");
}
index=-index-1;
entries.add(index, entry);
}
// test code check if the entry separator key the new min key ist ???
//
if(!path.isEmpty()){
// get entry pointing to this node
BPlusTree.IndexEntry indexEntry = (BPlusTree.IndexEntry)indexEntry(path);
// check descriptor
if(indexEntry.separator().compareTo(separator(entry)) > 0){
MVSeparator sepIndex = ((MVSeparator)indexEntry.separator());
MVSeparator entrySep = (MVSeparator)(separator(entry)).clone();
sepIndex.updateSepValue(entrySep.sepValue());
// get parent node if exists
MapEntry pathEntry = (MapEntry)path.pop();
if(!path.isEmpty()){
update(path);
}
path.push(pathEntry);
}
}
}
/*
* (non-Javadoc)
* @see xxl.core.indexStructures.MVBTree.Node#remove(int)
*/
protected Object remove(int index) {
if((index<0)||(index>= entries.size())) return null;
Object entry=getEntry(index);
Lifespan life;
if(level>0) {
((MVSeparator)((IndexEntry)entry).separator).delete(currentVersion());
life=((MVSeparator)((IndexEntry)entry).separator).lifespan();
}
else {
((LeafEntry)entry).lifespan.delete(currentVersion());
life=((LeafEntry)entry).lifespan;
}
if(life.endVersion().compareTo(life.beginVersion())<=0) super.remove(index);
return entry;
}
/**
*
* @return
*/
@SuppressWarnings("rawtypes")
public List getLiveEntries(){
return liveEntries;
}
/*
* (non-Javadoc)
* @see xxl.core.indexStructures.MVBTree.Node#versionSplit(xxl.core.indexStructures.MVBTree.Version, java.util.Stack, xxl.core.indexStructures.MVBTree.Node.SplitInfo)
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
protected SplitInfo versionSplit(Version splitVersion, Stack path, SplitInfo splitInfo) {
Node node = (Node)node(path);
IndexEntry indexEntry = (IndexEntry)indexEntry(path);
splitInfo.initVersionSplit(splitVersion, (MVSeparator)indexEntry.separator());
if (splitVersion.compareTo(((MVSeparator)indexEntry.separator).insertVersion()) < 0){
throw new IllegalArgumentException("Split of:"+ indexEntry+" at Version:"+splitVersion);
}
((MVSeparator)indexEntry.separator).delete(splitVersion); // delete
// new code
MapEntry pathEntry = (MapEntry)path.pop();
Node parentNode = (Node)node(path);
if(parentNode != null){
parentNode.removeFromLiveList(indexEntry);
parentNode.grow(indexEntry, new Stack());
}
path.push(pathEntry);
// new code
Iterator currentEntries = node.query(splitVersion);
if(level > 0 ){
for(Object entry : node.liveEntries){
liveEntries.add((IndexEntry)copyEntry(entry));
}
}else{
while (currentEntries.hasNext()) {
Object entry = currentEntries.next();
Object cpy = copyEntry(entry);
entries.add(cpy);
}
node.removeEntriesFromVersion(splitVersion);
}
if (level()==0) {
this.predecessors.clear();
IndexEntry predEntry = (IndexEntry)createIndexEntry(indexEntry.parentLevel);
MVSeparator predSep = (MVSeparator)separator(indexEntry).clone();
predSep.delete(splitVersion);
predEntry.initialize(indexEntry.id(), predSep);
this.predecessors.add(predEntry);
}
purgeQueue.enqueue(new BlockDelInfo(indexEntry.id(), splitVersion));
return splitInfo;
}
/*
* (non-Javadoc)
* @see xxl.core.indexStructures.MVBTree.Node#keySplit(java.util.Stack, xxl.core.indexStructures.MVBTree.Node.SplitInfo)
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
protected SplitInfo keySplit(Stack path, SplitInfo splitInfo) {
this.sortEntries(true);
List cpyEntries = null;
Node newNode = null;
if(level > 0 ){
cpyEntries = this.liveEntries.subList((this.liveEntries.size()+1)/2, this.liveEntries.size());
newNode = (Node)MVBT.this.createNode(level());
newNode.liveEntries.addAll(cpyEntries);
cpyEntries.clear();
}else{
cpyEntries = this.entries.subList((number()+1)/2, number());
newNode = (Node)MVBT.this.createNode(level());
newNode.entries.addAll(cpyEntries);
cpyEntries.clear();
this.sortEntries();
}
MVSeparator newSeparator = null;
if (level > 0){
newSeparator = (MVSeparator)separator(newNode.liveEntries.get(0)).clone();
newSeparator.union(separator((newNode.liveEntries.get(newNode.liveEntries.size()-1))));
newSeparator.setInsertVersion(splitInfo.splitVersion());
splitInfo.initKeySplit(newSeparator, newNode);
}else{
newSeparator = (MVSeparator)separator(newNode.getFirst()).clone();
newSeparator.union(separator(newNode.getLast()));
newSeparator.setInsertVersion(splitInfo.splitVersion());
newNode.sortEntries();
splitInfo.initKeySplit(newSeparator, newNode);
}
if (level()==0)
newNode.predecessors.add(this.predecessors.get(0));
return splitInfo;
}
protected SplitInfo strongMerge(Stack path, SplitInfo splitInfo) {
if (path.size() <= 1)
throw new IllegalStateException("There is no parent on the stack.");
Node node = (Node) splitInfo.versionSplitNode(); // created node
IndexEntry mergeSibling = determineStrongMergeSibling(path, splitInfo);
//test new code test
splitInfo.setSiblingMergeSeparator((MVSeparator)mergeSibling.separator);
MapEntry pathEntry = (MapEntry)path.pop();
down(path, mergeSibling);
Node tempNode = (Node)MVBT.this.createNode(level());
tempNode.versionSplit(splitInfo.splitVersion(), path);
if(level > 0 ){
// merge in linear time
List<IndexEntry> newLiveEntries = new ArrayList<>();
Iterator<IndexEntry> mergedEntries = new Merger<IndexEntry>(liveIndexEntryComparator, node.liveEntries.iterator(), tempNode.liveEntries.iterator());
while(mergedEntries.hasNext()){
IndexEntry idxEntry = mergedEntries.next();
newLiveEntries.add(idxEntry);
}
node.liveEntries = newLiveEntries;
}else{
node.entries.addAll(tempNode.entries);
node.sortEntries();
}
if (node.level() == 0)
node.predecessors.add(tempNode.predecessors.get(0));
update(path);
up(path);
path.push(pathEntry);
splitInfo.setMergePerformed();
assert node.countCurrentEntries() >= (level() == 0 ? getLeafNodeD() : getIndexNodeD());
return splitInfo;
}
@Override
public String toString() {
return "Node [predecessors=" + predecessors + ", liveEntries="
+ liveEntries + ", entries=" + entries + ", level="
+ level + "]";
}
}
/** This Converter is concerned with serializing of nodes to write or read them into
* or from the external storage.
*/
@SuppressWarnings("serial")
public class NodeConverter extends BPlusTree.NodeConverter {
public Object read (DataInput dataInput, Object object) throws IOException {
int level=dataInput.readInt();
int number=dataInput.readInt();
Node node = (Node)createNode(level);
readPredecessors(dataInput, node);
for (int i=0; i<number; i++) {
Object entry;
if(node.level()==0) entry= readLeafEntry(dataInput);
else entry=readIndexEntry(dataInput,node.level);
node.entries.add(entry);
}
int liveNumber = dataInput.readInt();
for( int i = 0; i < liveNumber; i++){
IndexEntry entry=readIndexEntry(dataInput,node.level);
node.liveEntries.add(entry);
}
return node;
}
public void write (DataOutput dataOutput, Object object) throws IOException {
Node node = (Node)object;
IntegerConverter.DEFAULT_INSTANCE.writeInt(dataOutput, node.level);
IntegerConverter.DEFAULT_INSTANCE.writeInt(dataOutput, node.entries.size());
writePredecessors(dataOutput, node);
for(int i=0; i<node.entries.size() ;i++) {
Object entry=node.getEntry(i);
if(node.level==0) writeLeafEntry(dataOutput,(LeafEntry)entry);
else writeIndexEntry(dataOutput,(IndexEntry)entry);
}
IntegerConverter.DEFAULT_INSTANCE.writeInt(dataOutput, node.liveEntries.size());
for(int i=0; i<node.liveEntries.size() ;i++) {
Object entry = node.liveEntries.get(i);
if(node.level==0) writeLeafEntry(dataOutput,(LeafEntry)entry);
else writeIndexEntry(dataOutput,(IndexEntry)entry);
}
}
public int headerSize() {
return 2*IntegerConverter.SIZE + predecessorsSize()+ IntegerConverter.SIZE;
}
protected void writePredecessors(DataOutput dataOutput, Node node) throws IOException {
IntegerConverter.DEFAULT_INSTANCE.writeInt(dataOutput, node.predecessors.size());
for(int i=0; i<node.predecessors.size(); i++) {
IndexEntry predecessor=(IndexEntry)node.predecessors.get(i);
writeIndexEntry(dataOutput, predecessor);
}
}
protected void readPredecessors(DataInput dataInput, Node node) throws IOException {
int count=IntegerConverter.DEFAULT_INSTANCE.readInt(dataInput);
for(int i=0; i<count; i++) {
IndexEntry predecessor= readIndexEntry(dataInput, 1);
node.predecessors.add(predecessor);
}
}
protected void writeMVRegion(DataOutput output, MVRegion region) throws IOException {
keyConverter.write(output, region.minBound());
keyConverter.write(output, region.maxBound());
versionConverter.write(output, region.beginVersion());
BooleanConverter.DEFAULT_INSTANCE.write(output, new Boolean(region.isDead()));
if(region.isDead()) versionConverter.write(output, region.endVersion());
}
protected MVRegion readMVRegion(DataInput input) throws IOException {
Comparable min=(Comparable)keyConverter.read(input, null);
Comparable max=(Comparable)keyConverter.read(input, null);
Version begin=(Version)versionConverter.read(input, null);
Version end=null;
boolean isDead=BooleanConverter.DEFAULT_INSTANCE.readBoolean(input);
if(isDead) end=(Version)versionConverter.read(input, null);
return createMVRegion(begin, end, min, max);
}
protected int mvRegionSize() {
return 2*keyConverter.getMaxObjectSize()+2*versionConverter.getMaxObjectSize()+BooleanConverter.SIZE;
}
protected int predecessorsSize() {
// Daniar: new code IntegerConverter.SIZE
return IntegerConverter.SIZE+2*(mvRegionSize()+container().getIdSize());
}
protected IndexEntry readIndexEntry(DataInput input, int parentLevel) throws IOException {
IndexEntry indexEntry=(IndexEntry)createIndexEntry(parentLevel);
Object id= container().objectIdConverter().read(input, null);
Comparable sepValue=(Comparable)keyConverter.read(input, null);
Lifespan life= (Lifespan)lifespanConverter.read(input, null);
MVSeparator mvSeparator= createMVSeparator(life.beginVersion(), life.endVersion(), sepValue);
indexEntry.initialize(id, mvSeparator);
return indexEntry;
}
protected void writeIndexEntry(DataOutput output, IndexEntry entry) throws IOException {
container().objectIdConverter().write(output, entry.id());
keyConverter.write(output, entry.separator.sepValue());
lifespanConverter.write(output, ((MVSeparator)entry.separator()).lifespan());
}
@Override
protected int indexEntrySize() {
return container().getIdSize()+keyConverter.getMaxObjectSize()+lifespanConverter.getMaxObjectSize();
}
public int maxIndexEntrySize(){
return indexEntrySize();
}
public int maxLeafEntrySize(){
return leafEntrySize() ;
}
protected LeafEntry readLeafEntry(DataInput input) throws IOException {
Lifespan life= (Lifespan)lifespanConverter.read(input, null);
Object data= dataConverter.read(input, null);
return new LeafEntry(life, data);
}
protected void writeLeafEntry(DataOutput output, LeafEntry entry) throws IOException {
lifespanConverter.write(output, entry.lifespan);
dataConverter.write(output, entry.data());
}
@Override
protected int leafEntrySize() {
return dataConverter.getMaxObjectSize()+lifespanConverter.getMaxObjectSize();
}
/**
* Computes the maximal size (in bytes) of a record (IndexEntry or data
* object). It calls the method getIdSize() of the tree container. If
* the tree container has not been initialized a NullPointerException is
* thrown.
*
* @return the maximal size of a record (IndexEntry or data object)
*/
public int getMaxRecordSize() {
return Math.max(dataConverter.getMaxObjectSize() + lifespanConverter.getMaxObjectSize(), indexEntrySize());
}
}
}