/* This file is part of the db4o object database http://www.db4o.com
Copyright (C) 2004 - 2011 Versant Corporation http://www.versant.com
db4o is free software; you can redistribute it and/or modify it under
the terms of version 3 of the GNU General Public License as published
by the Free Software Foundation.
db4o 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 General Public License
for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see http://www.gnu.org/licenses/. */
package com.db4o.internal.btree;
import com.db4o.*;
import com.db4o.foundation.*;
import com.db4o.internal.*;
import com.db4o.internal.ids.*;
import com.db4o.internal.slots.*;
import com.db4o.marshall.*;
/**
* We work with BTreeNode in two states:
*
* - deactivated: never read, no valid members, ID correct or 0 if new
* - write: real representation of keys, values and children in arrays
* The write state can be detected with canWrite(). States can be changed
* as needed with prepareRead() and prepareWrite().
*
* @exclude
*/
public final class BTreeNode extends LocalPersistentBase{
private static final int COUNT_LEAF_AND_3_LINK_LENGTH = (Const4.INT_LENGTH * 4) + 1;
private static final int SLOT_LEADING_LENGTH = Const4.LEADING_LENGTH + COUNT_LEAF_AND_3_LINK_LENGTH;
final BTree _btree;
private int _count;
private boolean _isLeaf;
private Object[] _keys;
/**
* Can contain BTreeNode or Integer for ID of BTreeNode
*/
private Object[] _children;
private int _parentID;
private int _previousID;
private int _nextID;
private boolean _dead;
/* Constructor for new nodes */
public BTreeNode(BTree btree,
int count,
boolean isLeaf,
int parentID,
int previousID,
int nextID){
_btree = btree;
_parentID = parentID;
_previousID = previousID;
_nextID = nextID;
_count = count;
_isLeaf = isLeaf;
prepareArrays();
}
/* Constructor for existing nodes, requires valid ID */
public BTreeNode(int id, BTree btree){
_btree = btree;
setID(id);
setStateDeactivated();
}
/* Constructor to create a new root from two nodes */
public BTreeNode(Transaction trans, BTreeNode firstChild, BTreeNode secondChild){
this(firstChild._btree, 2, false, 0, 0, 0);
_keys[0] = firstChild._keys[0];
_children[0] = firstChild;
_keys[1] = secondChild._keys[0];
_children[1] = secondChild;
write(trans.systemTransaction());
firstChild.setParentID(trans, getID());
secondChild.setParentID(trans, getID());
}
public BTree btree() {
return _btree;
}
/**
* @return the split node if this node is split
* or this if the first key has changed
*/
public BTreeNode add(Transaction trans, PreparedComparison preparedComparison, Object obj){
ByteArrayBuffer reader = prepareRead(trans);
Searcher s = search(trans, preparedComparison, reader);
if(_isLeaf){
prepareWrite(trans);
setStateDirty();
if (wasRemoved(trans, s)) {
cancelRemoval(trans, obj, s.cursor());
return null;
}
if(s.count() > 0 && ! s.beforeFirst()){
s.moveForward();
}
prepareInsert(s.cursor());
_keys[s.cursor()] = applyNewAddPatch(trans, obj);
}else{
BTreeNode childNode = child(reader, s.cursor());
BTreeNode childNodeOrSplit = childNode.add(trans, preparedComparison, obj);
if(childNodeOrSplit == null){
return null;
}
prepareWrite(trans);
setStateDirty();
_keys[s.cursor()] = childNode._keys[0];
if(childNode != childNodeOrSplit){
int splitCursor = s.cursor() + 1;
prepareInsert(splitCursor);
_keys[splitCursor] = childNodeOrSplit._keys[0];
_children[splitCursor] = childNodeOrSplit;
}
}
if (mustSplit()) {
return split(trans);
}
if (s.cursor() == 0) {
return this;
}
return null;
}
private boolean mustSplit() {
return _count >= _btree.nodeSize();
}
private BTreeAdd applyNewAddPatch(Transaction trans, Object obj) {
sizeIncrement(trans);
return new BTreeAdd(trans, obj);
}
private void cancelRemoval(Transaction trans, Object obj, int index) {
final BTreeUpdate patch = (BTreeUpdate)keyPatch(index);
BTreeUpdate nextPatch = patch.removeFor(trans);
_keys[index] = newCancelledRemoval(trans, patch.getObject(), obj, nextPatch);
sizeIncrement(trans);
}
private BTreePatch newCancelledRemoval(Transaction trans, Object originalObject, Object currentObject, BTreeUpdate existingPatches) {
return new BTreeCancelledRemoval(trans, originalObject, currentObject, existingPatches);
}
private void sizeIncrement(Transaction trans) {
_btree.sizeChanged(trans, this, 1);
}
private boolean wasRemoved(Transaction trans, Searcher s) {
if (!s.foundMatch()) {
return false;
}
BTreePatch patch = keyPatch(trans, s.cursor());
return patch != null && patch.isRemove();
}
BTreeNodeSearchResult searchLeaf(Transaction trans, PreparedComparison preparedComparison, SearchTarget target) {
ByteArrayBuffer reader = prepareRead(trans);
Searcher s = search(trans, preparedComparison, reader, target);
if(! _isLeaf){
return child(reader, s.cursor()).searchLeaf(trans, preparedComparison, target);
}
if(! s.foundMatch() || target == SearchTarget.ANY || target == SearchTarget.HIGHEST){
return new BTreeNodeSearchResult(trans, reader, btree(), s, this);
}
if(target == SearchTarget.LOWEST){
BTreeNodeSearchResult res = findLowestLeafMatch(trans, preparedComparison, s.cursor() - 1);
if(res != null){
return res;
}
return createMatchingSearchResult(trans, reader, s.cursor());
}
throw new IllegalStateException();
}
private BTreeNodeSearchResult findLowestLeafMatch(Transaction trans, PreparedComparison preparedComparison, int index){
return findLowestLeafMatch(trans, preparedComparison, prepareRead(trans), index);
}
private BTreeNodeSearchResult findLowestLeafMatch(Transaction trans, PreparedComparison preparedComparison, ByteArrayBuffer reader, int index){
if(index >= 0){
if(!compareEquals(preparedComparison, trans, reader, index)){
return null;
}
if(index > 0){
BTreeNodeSearchResult res = findLowestLeafMatch(trans, preparedComparison, reader, index - 1);
if(res != null){
return res;
}
return createMatchingSearchResult(trans, reader, index);
}
}
final BTreeNode node = previousNode();
if(node != null){
final ByteArrayBuffer nodeReader = node.prepareRead(trans);
BTreeNodeSearchResult res = node.findLowestLeafMatch(trans, preparedComparison, nodeReader, node.lastIndex());
if(res != null){
return res;
}
}
if(index < 0){
return null;
}
return createMatchingSearchResult(trans, reader, index);
}
private boolean compareEquals(PreparedComparison preparedComparison, Transaction trans, final ByteArrayBuffer reader, int index) {
if(canWrite()){
return compareInWriteMode(preparedComparison, index) == 0;
}
return compareInReadMode(trans, preparedComparison, reader, index) == 0;
}
private BTreeNodeSearchResult createMatchingSearchResult(Transaction trans, ByteArrayBuffer reader, int index) {
return new BTreeNodeSearchResult(trans, reader, btree(), this, index, true);
}
public boolean canWrite(){
return _keys != null;
}
BTreeNode child(int index){
if (_children[index] instanceof BTreeNode){
return (BTreeNode)_children[index];
}
return produceChild(index, ((Integer)_children[index]).intValue());
}
BTreeNode child(ByteArrayBuffer reader, int index){
if( childLoaded(index) ){
return (BTreeNode)_children[index];
}
return produceChild(index, childID(reader, index));
}
private BTreeNode produceChild(int index, int childID) {
BTreeNode child = _btree.produceNode(childID);
if(_children != null){
_children[index] = child;
}
return child;
}
private int childID(ByteArrayBuffer reader, int index){
if(_children == null){
seekChild(reader, index);
return reader.readInt();
}
return childID(index);
}
private int childID(int index){
if(childLoaded(index)){
return ((BTreeNode)_children[index]).getID();
}
return ((Integer)_children[index]).intValue();
}
private boolean childLoaded(int index){
if(_children == null){
return false;
}
return _children[index] instanceof BTreeNode;
}
private boolean childCanSupplyFirstKey(int index){
if(! childLoaded(index)){
return false;
}
return ((BTreeNode)_children[index]).canWrite();
}
public void commit(Transaction trans){
commitOrRollback(trans, true);
}
void commitOrRollback(Transaction trans, boolean isCommit){
if(DTrace.enabled){
DTrace.BTREE_NODE_COMMIT_OR_ROLLBACK.log(getID());
}
if(_dead){
return;
}
if(! _isLeaf){
return;
}
if(! isDirty(trans)){
return;
}
Object keyZero = _keys[0];
Object[] tempKeys = new Object[_btree.nodeSize()];
int count = 0;
for (int i = 0; i < _count; i++) {
Object key = _keys[i];
BTreePatch patch = keyPatch(i);
if(patch != null){
key = isCommit ? patch.commit(trans, _btree, this) : patch.rollback(trans, _btree);
}
if(key != No4.INSTANCE){
tempKeys[count] = key;
count ++;
}
}
_keys = tempKeys;
_count = count;
if(freeIfEmpty(trans)){
return;
}
setStateDirty();
// TODO: Merge nodes here on low _count value.
if(_keys[0] != keyZero){
tellParentAboutChangedKey(trans);
}
}
private boolean freeIfEmpty(Transaction trans){
return freeIfEmpty(trans, _count);
}
private boolean freeIfEmpty(Transaction trans, int count){
if(count > 0){
return false;
}
if(isRoot()){
return false;
}
free((LocalTransaction)trans);
return true;
}
private boolean isRoot() {
return _btree.root() == this;
}
public boolean equals(Object obj) {
if (this == obj){
return true;
}
if(! (obj instanceof BTreeNode)){
return false;
}
BTreeNode other = (BTreeNode) obj;
return getID() == other.getID();
}
public int hashCode() {
return getID();
}
public void free(LocalTransaction trans){
_dead = true;
if(! isRoot()){
BTreeNode parent = _btree.produceNode(_parentID);
parent.removeChild(trans, this);
}
pointPreviousTo(trans, _nextID);
pointNextTo(trans, _previousID);
super.free((LocalTransaction)trans);
_btree.removeNode(this);
_btree.notifyDeleted(trans, this);
}
void holdChildrenAsIDs(){
if(_children == null){
return;
}
for (int i = 0; i < _count; i++) {
if(_children[i] instanceof BTreeNode){
_children[i] = new Integer( ((BTreeNode)_children[i]).getID() );
}
}
}
private void removeChild(Transaction trans, BTreeNode child) {
prepareWrite(trans);
setStateDirty();
int id = child.getID();
for (int i = 0; i < _count; i++) {
if(childID(i) == id){
if(freeIfEmpty(trans, _count -1)){
return;
}
remove(i);
if(i < 1){
tellParentAboutChangedKey(trans);
}
if(_count == 0){
// root node empty case only, have to turn it into a leaf
_isLeaf = true;
}
return;
}
}
throw new IllegalStateException("child not found");
}
private void keyChanged(Transaction trans, BTreeNode child) {
prepareWrite(trans);
setStateDirty();
int id = child.getID();
for (int i = 0; i < _count; i++) {
if(childID(i) == id){
_keys[i] = child._keys[0];
_children[i] = child;
keyChanged(trans, i);
return;
}
}
throw new IllegalStateException("child not found");
}
private void tellParentAboutChangedKey(Transaction trans){
if(! isRoot()){
BTreeNode parent = _btree.produceNode(_parentID);
parent.keyChanged(trans, this);
}
}
private boolean isDirty(Transaction trans){
if(! canWrite()){
return false;
}
for (int i = 0; i < _count; i++) {
if(keyPatch(trans, i) != null){
return true;
}
}
return false;
}
private int compareInWriteMode(PreparedComparison preparedComparison, int index){
return - preparedComparison.compareTo(key(index));
}
private int compareInReadMode(Transaction trans, PreparedComparison preparedComparison, ByteArrayBuffer reader, int index){
seekKey(reader, index);
return - preparedComparison.compareTo(keyHandler().readIndexEntry(trans.context(), reader));
}
public int count() {
return _count;
}
private int entryLength(){
int len = keyHandler().linkLength();
if(!_isLeaf){
len += Const4.ID_LENGTH;
}
return len;
}
public int firstKeyIndex(Transaction trans) {
for (int ix = 0; ix < _count; ix++) {
if(indexIsValid(trans, ix)){
return ix;
}
}
return -1;
}
public int lastKeyIndex(Transaction trans) {
for (int ix = _count - 1; ix >= 0; ix--) {
if(indexIsValid(trans, ix)){
return ix;
}
}
return -1;
}
public boolean indexIsValid(Transaction trans, int index){
if(!canWrite()){
return true;
}
BTreePatch patch = keyPatch(index);
if(patch == null){
return true;
}
return patch.key(trans) != No4.INSTANCE;
}
private Object firstKey(Transaction trans){
final int index = firstKeyIndex(trans);
if (-1 == index) {
return No4.INSTANCE;
}
return internalKey(trans, index);
}
public byte getIdentifier() {
return Const4.BTREE_NODE;
}
private void prepareInsert(int pos){
if(pos > lastIndex()){
_count ++;
return;
}
int len = _count - pos;
System.arraycopy(_keys, pos, _keys, pos + 1, len);
if(_children != null){
System.arraycopy(_children, pos, _children, pos + 1, len);
}
_count++;
}
private void remove(int pos){
if(DTrace.enabled){
DTrace.BTREE_NODE_REMOVE.log(getID());
}
int len = _count - pos;
_count--;
System.arraycopy(_keys, pos + 1, _keys, pos, len);
_keys[_count] = null;
if(_children != null){
System.arraycopy(_children, pos + 1, _children, pos, len);
_children[_count] = null;
}
}
Object key(int index){
Object obj = _keys[index];
if( obj instanceof BTreePatch){
return ((BTreePatch)obj).getObject();
}
return obj;
}
public Object key(Transaction trans, int index){
return key(trans, prepareRead(trans), index);
}
Object key(Transaction trans, ByteArrayBuffer reader, int index){
if(canWrite()){
return internalKey(trans, index);
}
if(reader == null){
reader = prepareRead(trans);
}
if(canWrite()){
return internalKey(trans, index);
}
seekKey(reader, index);
return keyHandler().readIndexEntry(trans.context(), reader);
}
private Object internalKey(Transaction trans, int index){
BTreePatch patch = keyPatch(index);
if(patch == null){
return _keys[index];
}
return patch.key(trans);
}
private BTreePatch keyPatch(int index){
Object obj = _keys[index];
if( obj instanceof BTreePatch){
return (BTreePatch)obj;
}
return null;
}
BTreePatch keyPatch(Transaction trans, int index){
Object obj = _keys[index];
if( obj instanceof BTreePatch){
return ((BTreePatch)obj).forTransaction(trans);
}
return null;
}
private Indexable4 keyHandler(){
return _btree.keyHandler();
}
public int ownLength() {
return SLOT_LEADING_LENGTH
+ (_count * entryLength())
+ Const4.BRACKETS_BYTES;
}
ByteArrayBuffer prepareRead(Transaction trans){
BTreeNodeCacheEntry cacheEntry = btree().cacheEntry(this);
if(canWrite()){
return null;
}
if(isNew()){
return null;
}
Transaction systemTransaction = trans.systemTransaction();
ByteArrayBuffer buffer = cacheEntry.buffer();
if(buffer != null){
// Cache hit, still unread
buffer.seek(0);
read(systemTransaction, buffer);
cacheEntry.buffer(null);
_btree.addToProcessing(this);
return null;
}
buffer = produceReadBuffer(systemTransaction);
if (Deploy.debug) {
buffer.readBegin(getIdentifier());
}
readNodeHeader(buffer);
cacheEntry.buffer(buffer);
return buffer;
}
void prepareWrite(Transaction trans){
if(_dead){
return;
}
BTreeNodeCacheEntry cacheEntry = btree().cacheEntry(this);
if(canWrite()){
return;
}
ByteArrayBuffer buffer = cacheEntry.buffer();
if(buffer != null){
buffer.seek(0);
read(trans.systemTransaction(), buffer);
cacheEntry.buffer(null);
} else{
read(trans.systemTransaction());
}
_btree.addToProcessing(this);
}
private void prepareArrays(){
if(canWrite()){
return;
}
_keys = new Object[_btree.nodeSize()];
if(!_isLeaf){
_children = new Object[_btree.nodeSize()];
}
}
private void readNodeHeader(ByteArrayBuffer reader){
_count = reader.readInt();
byte leafByte = reader.readByte();
_isLeaf = (leafByte == 1);
_parentID = reader.readInt();
_previousID = reader.readInt();
_nextID = reader.readInt();
}
public void readThis(Transaction trans, ByteArrayBuffer reader) {
readNodeHeader(reader);
prepareArrays();
boolean isInner = ! _isLeaf;
for (int i = 0; i < _count; i++) {
_keys[i] = keyHandler().readIndexEntry(trans.context(), reader);
if(isInner){
_children[i] = new Integer(reader.readInt());
}
}
}
public void remove(Transaction trans, int index){
if(!_isLeaf){
throw new IllegalStateException();
}
prepareWrite(trans);
setStateDirty();
Object obj = null;
BTreePatch patch = keyPatch(index);
if(patch == null){
obj = _keys[index];
}else {
BTreePatch transPatch = patch.forTransaction(trans);
if(transPatch != null){
obj = transPatch.getObject();
} else {
// There could be more than one patch with different object
// identities. We have no means to determine a "best" object
// so we just take any one. Could be problematic.
obj = patch.getObject();
}
}
remove(trans, obj, index);
}
public boolean remove(Transaction trans, Object obj, int index){
if(!_isLeaf){
throw new IllegalStateException();
}
prepareWrite(trans);
setStateDirty();
BTreePatch patch = keyPatch(index);
// no patch, no problem, can remove
if(patch == null){
_keys[index] = applyNewRemovePatch(trans, obj);
keyChanged(trans, index);
return true;
}
BTreePatch transPatch = patch.forTransaction(trans);
if(transPatch != null){
if(transPatch.isAdd()){
cancelAdding(trans, index);
return true;
}
if(transPatch.isCancelledRemoval()){
BTreeRemove removePatch = applyNewRemovePatch(trans, transPatch.getObject());
_keys[index] = ((BTreeUpdate)patch).replacePatch(transPatch, removePatch);
keyChanged(trans, index);
return true;
}
}else{
// If the patch is a removal of a cancelled removal for another
// transaction, we need one for this transaction also.
if(! patch.isAdd()){
((BTreeUpdate)patch).append(applyNewRemovePatch(trans, obj));
return true;
}
}
return false;
}
public void remove(Transaction trans, PreparedComparison preparedComparison, Object obj, int index){
if(remove(trans, obj, index)){
return;
}
// now we try if removal is OK for the next element in this node
if(index != lastIndex()){
if(compareInWriteMode(preparedComparison, index + 1 ) != 0){
return;
}
remove(trans, preparedComparison, obj, index + 1);
return;
}
// nothing else worked so far, move on to the next node, try there
BTreeNode node = nextNode();
if(node == null){
return;
}
node.prepareWrite(trans);
if(node.compareInWriteMode(preparedComparison, 0) != 0){
return;
}
node.remove(trans, preparedComparison, obj, 0);
}
private void cancelAdding(Transaction trans, int index) {
_btree.notifyRemoveListener(new TransactionContext(trans, keyPatch(index).getObject()));
if(freeIfEmpty(trans, _count-1)){
sizeDecrement(trans);
return;
}
remove(index);
keyChanged(trans, index);
sizeDecrement(trans);
}
private void sizeDecrement(Transaction trans) {
_btree.sizeChanged(trans, this, -1);
}
private int lastIndex() {
return _count - 1;
}
private BTreeRemove applyNewRemovePatch(Transaction trans, Object key) {
sizeDecrement(trans);
return new BTreeRemove(trans, key);
}
private void keyChanged(Transaction trans, int index) {
if(index == 0){
tellParentAboutChangedKey(trans);
}
}
void rollback(Transaction trans){
commitOrRollback(trans, false);
}
private Searcher search(Transaction trans, PreparedComparison preparedComparison, ByteArrayBuffer reader){
return search(trans, preparedComparison, reader, SearchTarget.ANY);
}
private Searcher search(Transaction trans, PreparedComparison preparedComparison, ByteArrayBuffer reader, SearchTarget target){
Searcher s = new Searcher(target, _count);
if(canWrite()){
while(s.incomplete()){
s.resultIs( compareInWriteMode(preparedComparison, s.cursor()));
}
}else{
while(s.incomplete()){
s.resultIs( compareInReadMode(trans, preparedComparison, reader, s.cursor()));
}
}
return s;
}
private void seekAfterKey(ByteArrayBuffer reader, int ix){
seekKey(reader, ix);
reader._offset += keyHandler().linkLength();
}
private void seekChild(ByteArrayBuffer reader, int ix){
seekAfterKey(reader, ix);
}
private void seekKey(ByteArrayBuffer reader, int ix){
reader._offset = SLOT_LEADING_LENGTH + (entryLength() * ix);
}
private BTreeNode split(Transaction trans){
BTreeNode res = new BTreeNode(_btree, _btree._halfNodeSize, _isLeaf,_parentID, getID(), _nextID);
System.arraycopy(_keys, _btree._halfNodeSize, res._keys, 0, _btree._halfNodeSize);
for (int i = _btree._halfNodeSize; i < _keys.length; i++) {
_keys[i] = null;
}
if(_children != null){
res._children = new Object[_btree.nodeSize()];
System.arraycopy(_children, _btree._halfNodeSize, res._children, 0, _btree._halfNodeSize);
for (int i = _btree._halfNodeSize; i < _children.length; i++) {
_children[i] = null;
}
}
_count = _btree._halfNodeSize;
res.write(trans.systemTransaction());
_btree.addNode(res);
int splitID = res.getID();
pointNextTo(trans, splitID);
setNextID(trans, splitID);
if(_children != null){
for (int i = 0; i < _btree._halfNodeSize; i++) {
if(res._children[i] == null){
break;
}
res.child(i).setParentID(trans, splitID );
}
}
_btree.notifySplit(trans, this, res);
return res;
}
private void pointNextTo(Transaction trans, int id){
if(_nextID != 0){
nextNode().setPreviousID(trans, id);
}
}
private void pointPreviousTo(Transaction trans, int id){
if(_previousID != 0){
previousNode().setNextID(trans, id);
}
}
public BTreeNode previousNode() {
if(_previousID == 0){
return null;
}
return _btree.produceNode(_previousID);
}
public BTreeNode nextNode() {
if(_nextID == 0){
return null;
}
return _btree.produceNode(_nextID);
}
BTreePointer firstPointer(Transaction trans) {
ByteArrayBuffer reader = prepareRead(trans);
if (_isLeaf) {
return leafFirstPointer(trans, reader);
}
return branchFirstPointer(trans, reader);
}
private BTreePointer branchFirstPointer(Transaction trans, ByteArrayBuffer reader) {
for (int i = 0; i < _count; i++) {
BTreePointer childFirstPointer = child(reader, i).firstPointer(trans);
if(childFirstPointer != null){
return childFirstPointer;
}
}
return null;
}
private BTreePointer leafFirstPointer(Transaction trans, ByteArrayBuffer reader) {
int index = firstKeyIndex(trans);
if(index == -1){
return null;
}
return new BTreePointer(trans, reader, this, index);
}
public BTreePointer lastPointer(Transaction trans) {
ByteArrayBuffer reader = prepareRead(trans);
if (_isLeaf) {
return leafLastPointer(trans, reader);
}
return branchLastPointer(trans, reader);
}
private BTreePointer branchLastPointer(Transaction trans, ByteArrayBuffer reader) {
for (int i = _count - 1; i >= 0; i--) {
BTreePointer childLastPointer = child(reader, i).lastPointer(trans);
if(childLastPointer != null){
return childLastPointer;
}
}
return null;
}
private BTreePointer leafLastPointer(Transaction trans, ByteArrayBuffer reader) {
int index = lastKeyIndex(trans);
if(index == -1){
return null;
}
return new BTreePointer(trans, reader, this, index);
}
public void purge(){
if(_dead){
_keys = null;
_children = null;
return;
}
if(! isPatched()){
return;
}
holdChildrenAsIDs();
_btree.addNode(this);
}
private boolean isPatched(){
if(_dead){
return false;
}
if(!canWrite()){
return false;
}
for (int i = 0; i < _count; i++) {
if(_keys[i] instanceof BTreePatch){
return true;
}
}
return false;
}
private void setParentID(Transaction trans, int id){
prepareWrite(trans);
setStateDirty();
_parentID = id;
}
private void setPreviousID(Transaction trans, int id){
prepareWrite(trans);
setStateDirty();
_previousID = id;
}
private void setNextID(Transaction trans, int id){
prepareWrite(trans);
setStateDirty();
_nextID = id;
}
public void traverseKeys(Transaction trans, Visitor4 visitor){
ByteArrayBuffer reader = prepareRead(trans);
if(_isLeaf){
for (int i = 0; i < _count; i++) {
Object obj = key(trans,reader, i);
if(obj != No4.INSTANCE){
visitor.visit(obj);
}
}
}else{
for (int i = 0; i < _count; i++) {
child(reader,i).traverseKeys(trans, visitor);
}
}
}
@Override
public boolean writeObjectBegin() {
if(_dead){
return false;
}
if(!canWrite()){
return false;
}
return super.writeObjectBegin();
}
public void writeThis(Transaction trans, ByteArrayBuffer buffer) {
int count = 0;
int startOffset = buffer._offset;
final Context context = trans.context();
buffer.incrementOffset(COUNT_LEAF_AND_3_LINK_LENGTH);
if(_isLeaf){
for (int i = 0; i < _count; i++) {
Object obj = internalKey(trans, i);
if(obj != No4.INSTANCE){
count ++;
keyHandler().writeIndexEntry(context, buffer, obj);
}
}
}else{
for (int i = 0; i < _count; i++) {
if(childCanSupplyFirstKey(i)){
BTreeNode child = (BTreeNode)_children[i];
Object childKey = child.firstKey(trans);
if(childKey != No4.INSTANCE){
count ++;
keyHandler().writeIndexEntry(context, buffer, childKey);
buffer.writeIDOf(trans, child);
}
}else{
count ++;
keyHandler().writeIndexEntry(context, buffer, key(i));
buffer.writeIDOf(trans, _children[i]);
}
}
}
int endOffset = buffer._offset;
buffer._offset = startOffset;
buffer.writeInt(count);
buffer.writeByte( _isLeaf ? (byte) 1 : (byte) 0);
buffer.writeInt(_parentID);
buffer.writeInt(_previousID);
buffer.writeInt(_nextID);
buffer._offset = endOffset;
}
public String toString() {
if(_count == 0){
return "Node " + getID() + " not loaded";
}
String str = "\nBTreeNode";
str += "\nid: " + getID();
str += "\nparent: " + _parentID;
str += "\nprevious: " + _previousID;
str += "\nnext: " + _nextID;
str += "\ncount:" + _count;
str += "\nleaf:" + _isLeaf + "\n";
if(canWrite()){
str += " { ";
boolean first = true;
for (int i = 0; i < _count; i++) {
if(_keys[i] != null){
if(! first){
str += ", ";
}
str += _keys[i].toString();
first = false;
}
}
str += " }";
}
return str;
}
public void debugLoadFully(Transaction trans) {
prepareWrite(trans);
if (_isLeaf) {
return;
}
for (int i=0; i<_count; ++i) {
if(_children[i] instanceof Integer){
_children[i] = btree().produceNode(((Integer)_children[i]).intValue());
}
((BTreeNode)_children[i]).debugLoadFully(trans);
}
}
public static void defragIndex(DefragmentContextImpl context,Indexable4 keyHandler) {
if (Deploy.debug) {
context.readBegin(Const4.BTREE_NODE);
}
// count
int count=context.readInt();
// leafByte
byte leafByte = context.readByte();
boolean isLeaf = (leafByte == 1);
context.copyID(); // parent ID
context.copyID(); // previous ID
context.copyID(); // next ID
for (int i = 0; i < count; i++) {
keyHandler.defragIndexEntry(context);
if(!isLeaf){
context.copyID();
}
}
if (Deploy.debug) {
context.readEnd();
}
}
public boolean isLeaf() {
return _isLeaf;
}
/** This traversal goes over all nodes, not just leafs */
void traverseAllNodes(Transaction trans, Visitor4 command) {
ByteArrayBuffer reader = prepareRead(trans);
command.visit(this);
if(_isLeaf){
return;
}
for (int childIdx=0;childIdx<_count;childIdx++) {
child(reader, childIdx).traverseAllNodes(trans, command);
}
}
public int size(Transaction trans) {
prepareRead(trans);
if(! canWrite()){
return _count;
}
int size = 0;
for (int i = 0; i < _count; i++) {
BTreePatch keyPatch = keyPatch(i);
if(keyPatch != null){
size += keyPatch.sizeDiff(trans);
}else{
size++;
}
}
return size;
}
@Override
public SlotChangeFactory slotChangeFactory() {
return _btree.slotChangeFactory();
}
@Override
public TransactionalIdSystem idSystem(Transaction trans) {
return _btree.idSystem(trans);
}
public void toReadMode(){
if(isNew()){
return;
}
if(! canWrite()){
return;
}
if(isDirty()){
return;
}
if(isPatched()){
return;
}
_keys = null;
_children = null;
}
}