/* 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.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Stack;
import xxl.core.collections.ReversedList;
import xxl.core.collections.containers.Container;
import xxl.core.cursors.Cursors;
import xxl.core.cursors.filters.Taker;
import xxl.core.cursors.mappers.Mapper;
import xxl.core.cursors.sources.Enumerator;
import xxl.core.cursors.sources.Inductor;
import xxl.core.cursors.unions.Sequentializer;
import xxl.core.functions.AbstractFunction;
import xxl.core.functions.Function;
/** This class implements a {@link BTree} which stores its leaf-nodes on multiple disks.
*/
public class MultiDiskBTree extends BTree {
/** The containers used in this <tt>MultiDiskBTree</tt>.
*/
protected Container [] leafContainers;
/** Function returning the container of a leaf if invoked with an <tt>Level1IndexEntry</tt>.
*/
protected Function multiDiskGetContainer = new AbstractFunction() {
public Object invoke (Object indexEntry) {
return leafContainers[((Level1IndexEntry)indexEntry).containersIndex];
}
};
/** Function determining which Container to use if invoked with a <tt>SplitInfo</tt>.
*/
protected Function multiDiskDetermineContainer = new AbstractFunction() {
public Object invoke (Object object) {
Node.SplitInfo splitInfo = (Node.SplitInfo)object;
Stack path = splitInfo.path;
if (path.isEmpty())
return leafContainers[0];
else {
final Tree.IndexEntry indexEntry = indexEntry(path);
final boolean [] excluded = new boolean[leafContainers.length];
Function exclude = new AbstractFunction() {
public Object invoke (Object indexEntry) {
excluded[((Level1IndexEntry)indexEntry).containersIndex] = true;
return null;
}
};
Object pathElement = path.pop();
if (path.isEmpty())
exclude.invoke(indexEntry);
else {
final Node parentNode = (Node)node(path);
final int index = ((List)parentNode.entries).indexOf(indexEntry)+1;
for (int i=0; i<2; i++) {
final int dir = i;
Cursors.forEach(
exclude,
new Taker(
new Sequentializer(
new Mapper(
new AbstractFunction() {
public Object invoke (Object node) {
List entryList = (List)((Node)node).entries;
int from = node==parentNode? index: 0;
int to = entryList.size();
return (dir==1?
entryList:
new ReversedList(entryList)
).subList(dir==1? from: to-from, to).iterator();
}
}
,new Inductor(
new AbstractFunction() {
public Object invoke (Object node) {
return ((Node)node).siblingsContainers[dir]!=null? Boolean.TRUE: Boolean.FALSE;
}
},
new AbstractFunction() {
public Object invoke (Object node) {
return ((Node)node).siblingsContainers[dir].get(((Node)node).siblingsIds[dir]);
}
},parentNode
))
),
(leafContainers.length-i)/2
)
);
}
}
path.push(pathElement);
return leafContainers[((Integer)Cursors.minima(new Enumerator(leafContainers.length),
new AbstractFunction() {
public Object invoke (Object object) {
int containersIndex = ((Integer)object).intValue();
return new Integer(
excluded[containersIndex]?
Integer.MAX_VALUE:
leafContainers[containersIndex].size()
);
}
}
).getFirst()).intValue()];
}
}
};
/** Initializes the tree.
*
* @param rootEntry the new {@link Tree#rootEntry}
* @param getDescriptor the new {@link Tree#getDescriptor}
* @param container the new {@link Tree#determineContainer}
* @param leafContainers the new {@link #leafContainers}
* @param minCapacity is used to define {@link Tree#underflows}, {@link Tree#getSplitMinRatio}
* and {@link Tree#getSplitMaxRatio}
* @param maxCapacity is used to define {@link Tree#overflows}
* @return the initialized tree
*/
public MultiDiskBTree initialize (IndexEntry rootEntry, Function getDescriptor, Container container, Container [] leafContainers, final int minCapacity, final int maxCapacity) {
super.initialize(rootEntry, getDescriptor, container, minCapacity, maxCapacity);
return initialize(leafContainers, getContainer, determineContainer);
}
/** Initializes the tree without adding an entry.
*
* @param getDescriptor the new {@link Tree#getDescriptor}
* @param container the new {@link Tree#determineContainer}
* @param leafContainers the new {@link #leafContainers}
* @param minCapacity is used to define {@link Tree#underflows}, {@link Tree#getSplitMinRatio}
* and {@link Tree#getSplitMaxRatio}
* @param maxCapacity is used to define {@link Tree#overflows}
* @return the initialized tree
*/
public MultiDiskBTree initialize (Function getDescriptor, Container container, Container [] leafContainers, final int minCapacity, final int maxCapacity) {
return initialize(null, getDescriptor, container, leafContainers, minCapacity, maxCapacity);
}
/** Initializes the tree with containers for non-leaf nodes.
*
* @param leafContainers the new {@link #leafContainers}
* @param superGetContainer the new {@link Tree#getContainer} for level>0
* @param superDetermineContainer the new {@link Tree#determineContainer} for level>0
* @return the initialized tree
*/
public MultiDiskBTree initialize (Container [] leafContainers, final Function superGetContainer, final Function superDetermineContainer) {
this.leafContainers = leafContainers;
this.getContainer =
new AbstractFunction() {
public Object invoke (Object indexEntry) {
return (((IndexEntry)indexEntry).level()>0?
superGetContainer:
multiDiskGetContainer)
.invoke(indexEntry);
}
};
this.determineContainer =
new AbstractFunction() {
public Object invoke (Object object) {
return (((Node.SplitInfo)object).newNode().level>0?
superDetermineContainer:
multiDiskDetermineContainer)
.invoke(object);
}
};
return this;
}
/* (non-Javadoc)
* @see xxl.core.indexStructures.Tree#createIndexEntry(int)
*/
public Tree.IndexEntry createIndexEntry (int parentLevel) {
return parentLevel>2?
super.createIndexEntry(parentLevel):
parentLevel==2?
(Tree.IndexEntry)new Level2IndexEntry(parentLevel):
(Tree.IndexEntry)new Level1IndexEntry(parentLevel);
}
/** This class describes the normal index entries (i.e. the entries of the non-leaf nodes with level>1)
* of a <tt>MultiDiskBTree</tt>. Each index entry refers to a {@link Tree.Node node} which is the root
* of the subtree. We call this node the subnode of the index entry.
*
* @see ORTree.IndexEntry
* @see MultiDiskBTree.Level1IndexEntry
*/
public class Level2IndexEntry extends ORTree.IndexEntry {
/** Creates a new <tt>Level2IndexEntry</tt> with a given {@link Tree.IndexEntry#parentLevel parent level}.
*
* @param parentLevel the parent level of the new <tt>IndexEntry</tt>
*/
public Level2IndexEntry (int parentLevel) {
super(parentLevel);
}
/* (non-Javadoc)
* @see xxl.core.indexStructures.Tree.IndexEntry#initialize(xxl.core.collections.containers.Container, java.lang.Object, xxl.core.indexStructures.Tree.Node.SplitInfo)
*/
public Tree.IndexEntry initialize (Container container, Object id, Tree.Node.SplitInfo splitInfo) {
super.initialize(container, id, splitInfo);
if (!splitInfo.path.isEmpty()) {
Node node = (Node)node(splitInfo.path);
if (node.siblingsContainers[1]!=null) {
Node siblingsNode = (Node)node.siblingsContainers[1].get(node.siblingsIds[1], false);
siblingsNode.siblingsContainers[0] = container;
siblingsNode.siblingsIds[0] = id;
node.siblingsContainers[1].update(node.siblingsIds[1], siblingsNode);
}
node.siblingsContainers[1] = container;
node.siblingsIds[1] = id;
}
return this;
}
}
/** This class describes the index entries for level 1 (i.e. the entries of the non-leaf nodes with level=1)
* of a <tt>MultiDiskBTree</tt>. Each index entry refers to a {@link Tree.Node node} which is the root
* of the subtree. We call this node the subnode of the index entry.
*
* @see ORTree.IndexEntry
* @see MultiDiskBTree.Level2IndexEntry
*/
public class Level1IndexEntry extends ORTree.IndexEntry {
/** The index of the container containing the Node this indexEntry points at.
*/
protected int containersIndex;
/** Creates a new <tt>Level1IndexEntry</tt> with a given {@link Tree.IndexEntry#parentLevel parent level}.
*
* @param parentLevel the parent level of the new <tt>IndexEntry</tt>
*/
public Level1IndexEntry (int parentLevel) {
super(parentLevel);
}
/* (non-Javadoc)
* @see xxl.core.indexStructures.Tree.IndexEntry#initialize(xxl.core.collections.containers.Container, java.lang.Object)
*/
public Tree.IndexEntry initialize (Container container, Object id) {
super.initialize(container, id);
this.containersIndex = Arrays.asList(leafContainers).indexOf(container);
return this;
}
}
/* (non-Javadoc)
* @see xxl.core.indexStructures.Tree#createNode(int)
*/
public Tree.Node createNode (int level) {
return level==1? new Node().initialize(level, new ArrayList()): super.createNode(level);
}
/** <tt>Node</tt> is the class used to represent leaf- and non-leaf nodes of <tt>MultiDiskBTree</tt>.
* Nodes are stored in containers.
*
* @see Tree.Node
* @see ORTree.Node
* @see MultiDiskBTree.Node
*/
public class Node extends BTree.Node {
/** The containers containing the siblings of this node.
*/
protected Container [] siblingsContainers = new Container[2];
/** The ids of the containers containing the siblings of this node.
*/
protected Object [] siblingsIds = new Object[2];
/* (non-Javadoc)
* @see xxl.core.indexStructures.Tree.Node#split(java.util.Stack)
*/
protected Tree.Node.SplitInfo split (Stack path) {
Tree.Node.SplitInfo splitInfo = super.split(path);
Tree.IndexEntry indexEntry = indexEntry(path);
siblingsContainers[0] = indexEntry.container();
siblingsIds[0] = indexEntry.id();
siblingsContainers[1] = siblingsContainers[1];
siblingsIds[1] = siblingsIds[1];
return splitInfo;
}
}
}