/* 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.defragment;
import java.io.*;
import com.db4o.foundation.*;
import com.db4o.internal.*;
import com.db4o.internal.btree.*;
import com.db4o.internal.ids.*;
import com.db4o.internal.mapping.*;
import com.db4o.internal.slots.*;
/**
* Database based mapping for IDs during a defragmentation run.
* Use this mapping to keep memory consumption lower than when
* using the {@link InMemoryIdMapping}.
*
* @see Defragment
*/
public class DatabaseIdMapping extends AbstractIdMapping {
private String _fileName;
private LocalObjectContainer _mappingDb;
private BTree _idTree;
private BTree _slotTree;
private MappedIDPair _cache = new MappedIDPair(0, 0);
private BTreeSpec _treeSpec=null;
private int _commitFrequency=0; // <=0 : never commit
private int _idInsertCount=0;
private int _slotInsertCount=0;
/**
* Will maintain the ID mapping as a BTree in the file with the given path.
* If a file exists in this location, it will be DELETED.
*
* Node size and cache height of the tree will be the default values used by
* the BTree implementation. The tree will never commit.
*
* @param fileName The location where the BTree file should be created.
*/
public DatabaseIdMapping(String fileName) {
this(fileName,null,0);
}
/**
* Will maintain the ID mapping as a BTree in the file with the given path.
* If a file exists in this location, it will be DELETED.
*
* @param fileName The location where the BTree file should be created.
* @param nodeSize The size of a BTree node
* @param commitFrequency The number of inserts after which a commit should be issued (<=0: never commit)
*/
public DatabaseIdMapping(String fileName,int nodeSize,int commitFrequency) {
this(fileName,new BTreeSpec(nodeSize),commitFrequency);
}
private DatabaseIdMapping(String fileName,BTreeSpec treeSpec,int commitFrequency) {
_fileName = fileName;
_treeSpec=treeSpec;
_commitFrequency=commitFrequency;
}
public int mappedId(int oldID) {
if (_cache.orig() == oldID) {
return _cache.mapped();
}
int classID = mappedClassID(oldID);
if (classID != 0) {
return classID;
}
BTreeRange range = _idTree.searchRange(trans(), new MappedIDPair(oldID, 0));
Iterator4 pointers = range.pointers();
if (pointers.moveNext()) {
BTreePointer pointer = (BTreePointer) pointers.current();
_cache = (MappedIDPair) pointer.key();
return _cache.mapped();
}
return 0;
}
protected void mapNonClassIDs(int origID, int mappedID) {
_cache = new MappedIDPair(origID, mappedID);
_idTree.add(trans(), _cache);
if(_commitFrequency > 0) {
_idInsertCount++;
if(_commitFrequency ==_idInsertCount) {
_idTree.commit(trans());
_idInsertCount = 0;
}
}
}
public void open() throws IOException {
_mappingDb = DefragmentServicesImpl.freshTempFile(_fileName,1);
_idTree = (_treeSpec == null ? new BTree(trans(), 0, new MappedIDPairHandler()) : new BTree(trans(), 0, new MappedIDPairHandler(), _treeSpec.nodeSize()));
_slotTree = (_treeSpec == null ? new BTree(trans(), 0, new BTreeIdSystem.IdSlotMappingHandler()) : new BTree(trans(), 0, new BTreeIdSystem.IdSlotMappingHandler(), _treeSpec.nodeSize()));
}
public void close() {
_mappingDb.close();
}
private Transaction trans() {
return _mappingDb.systemTransaction();
}
private static class BTreeSpec {
private int _nodeSize;
public BTreeSpec(int nodeSize) {
_nodeSize = nodeSize;
}
public int nodeSize() {
return _nodeSize;
}
}
public void mapId(int id, Slot slot) {
_slotTree.add(trans(), new IdSlotMapping(id, slot.address(), slot.length()));
if(_commitFrequency > 0) {
_slotInsertCount++;
if(_commitFrequency == _slotInsertCount) {
_slotTree.commit(trans());
_slotInsertCount = 0;
}
}
}
public Visitable<SlotChange> slotChanges() {
return new Visitable<SlotChange>() {
public void accept(final Visitor4<SlotChange> outSideVisitor) {
_slotTree.traverseKeys(trans(), new Visitor4<IdSlotMapping>() {
public void visit(IdSlotMapping idSlotMapping) {
SlotChange slotChange = new SlotChange(idSlotMapping._id);
slotChange.notifySlotCreated(idSlotMapping.slot());
outSideVisitor.visit(slotChange);
}
});
}
};
}
public int addressForId(int id){
BTreeRange range = _slotTree.searchRange(trans(), new IdSlotMapping(id, 0, 0));
Iterator4 pointers = range.pointers();
if (pointers.moveNext()) {
BTreePointer pointer = (BTreePointer) pointers.current();
return ((IdSlotMapping)pointer.key())._address;
}
return 0;
}
public void commit() {
_mappingDb.commit();
}
}