package org.wonderdb.core.collection.impl;
/*******************************************************************************
* Copyright 2013 Vilas Athavale
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.wonderdb.block.Block;
import org.wonderdb.block.BlockEntryPosition;
import org.wonderdb.block.BlockManager;
import org.wonderdb.cache.impl.CacheEntryPinner;
import org.wonderdb.core.collection.ResultIterator;
import org.wonderdb.types.TypeMetadata;
import org.wonderdb.types.record.Record;
public abstract class BaseResultIteratorImpl implements ResultIterator {
protected BlockEntryPosition currentPosn = null;
private boolean writeLockBlock = false;
protected Set<Object> pinnedBlocks = null;
private Set<Block> changedBlocks = null;
private List<Block> lockedBlocks = null;
TypeMetadata meta = null;
boolean firstTime = true;
public BaseResultIteratorImpl(BlockEntryPosition bep, boolean writeLock, TypeMetadata meta) {
this(bep, writeLock, new HashSet<Block>(), meta);
}
public BaseResultIteratorImpl(BlockEntryPosition bep, boolean blockWriteLock, Set<Block> changedBlocks, TypeMetadata meta) {
// block is already locked
this.meta = meta;
lockedBlocks = new ArrayList<Block>();
writeLockBlock = blockWriteLock;
this.pinnedBlocks = new HashSet<Object>();
this.changedBlocks = changedBlocks;
if (bep == null || bep.getBlock() == null) {
return;
}
currentPosn = new BlockEntryPosition(bep.getBlock(), bep.getPosn());
lockedBlocks.add(bep.getBlock());
CacheEntryPinner.getInstance().pin(bep.getBlockPtr(), pinnedBlocks);
}
public Record next() {
Record record = currentPosn.getBlock().getData().get(currentPosn.getPosn());
return record;
}
public Record peek() {
Record record = null;
if (currentPosn != null && currentPosn.getBlock() != null && currentPosn.getBlock().getData().size() > currentPosn.getPosn()) {
record = currentPosn.getBlock().getData().get(currentPosn.getPosn());
}
return record;
}
public boolean hasNext() {
if (currentPosn == null) {
return false;
}
if (!firstTime) {
currentPosn.setPosn(currentPosn.getPosn()+1);
} else {
firstTime = false;
}
Block currentBlock = currentPosn.getBlock();
if (currentBlock == null) {
return false;
}
int posn = currentPosn.getPosn();
if (posn < 0) {
return false;
}
while (currentBlock != null && currentBlock.getData().size() <= posn && currentBlock.getNext() != null) {
Block tmp = BlockManager.getInstance().getBlock(currentBlock.getNext(), meta, pinnedBlocks);
if (tmp != null) {
lock(tmp);
currentBlock = tmp;
currentPosn.setBlock(currentBlock);
currentPosn.setPosn(0);
posn = 0;
}
}
return currentBlock != null && currentBlock.getData().size() > posn;
}
public void remove() {
if (currentPosn != null && currentPosn.getBlock() != null) {
changedBlocks.add(currentPosn.getBlock());
currentPosn.getBlock().getData().remove(currentPosn.getPosn());
}
}
public void lock(Block block) {
if (currentPosn.getBlock() == block || block == null) {
return;
}
if (writeLockBlock) {
block.writeLock();
} else {
block.readLock();
}
List<Block> removedBlocks = new ArrayList<Block>();
Set<Object> blocksToUnpin = new HashSet<Object>();
for (int i = 0; i < lockedBlocks.size(); i++) {
Block b = lockedBlocks.get(i);
if (b != block) {
if (writeLockBlock) {
b.writeUnlock();
} else {
b.readUnlock();
}
removedBlocks.add(b);
blocksToUnpin.add(b.getPtr());
}
}
lockedBlocks.removeAll(removedBlocks);
lockedBlocks.add(block);
CacheEntryPinner.getInstance().unpin(blocksToUnpin, pinnedBlocks);
}
public void unlock() {
unlock(true);
}
public void unlock(boolean shouldUnpin) {
Set<Object> unpinPtrs = new HashSet<Object>();
for (int i = 0; i < lockedBlocks.size(); i++) {
if (writeLockBlock) {
lockedBlocks.get(i).writeUnlock();
} else {
lockedBlocks.get(i).readUnlock();
}
unpinPtrs.add(lockedBlocks.get(i).getPtr());
}
if (shouldUnpin) {
CacheEntryPinner.getInstance().unpin(unpinPtrs, pinnedBlocks);
}
}
public void insert(Record c) {
if (currentPosn == null) {
throw new RuntimeException("null current Position");
}
Block currentBlock = currentPosn.getBlock();
if (currentBlock == null) {
throw new RuntimeException("null current Block");
}
changedBlocks.add(currentBlock);
if (currentPosn.getPosn() < 0) {
currentBlock.getData().add(c);
} else {
currentBlock.getData().add(currentPosn.getPosn(), c);
}
}
public boolean isAnyBlockEmpty() {
if (currentPosn != null && currentPosn.getBlock() != null && currentPosn.getBlock().getData().size() == 0) {
return true;
}
return false;
}
public Block getCurrentBlock() {
if (currentPosn != null) {
return currentPosn.getBlock();
}
return null;
}
public Set<Object> getPinnedSet() {
return pinnedBlocks;
}
@Override
public TypeMetadata getTypeMetadata() {
return meta;
}
}