/*
* Copyright 2009-2016 Tilmann Zaeschke. All rights reserved.
*
* This file is part of ZooDB.
*
* ZooDB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ZooDB 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 ZooDB. If not, see <http://www.gnu.org/licenses/>.
*
* See the README and COPYING files for further information.
*/
package org.zoodb.internal.server.index;
import java.util.ArrayList;
import java.util.NoSuchElementException;
import org.zoodb.internal.server.index.LLIterator.IteratorPos;
import org.zoodb.internal.server.index.LongLongIndex.LLEntry;
/**
* Descending iterator.
* @author Tilmann Zaeschke
*/
class LLDescendingIterator extends AbstractPageIterator<LongLongIndex.LLEntry> {
private LLIndexPage currentPage = null;
private short currentPos = 0;
private final long minKey;
private final long maxKey;
private final ArrayList<IteratorPos> stack = new ArrayList<IteratorPos>(20);
private long nextKey;
private long nextValue;
private boolean hasValue = false;
public LLDescendingIterator(AbstractPagedIndex ind, long maxKey, long minKey) {
super(ind);
this.minKey = minKey;
this.maxKey = maxKey;
this.currentPage = (LLIndexPage) ind.getRoot();
this.currentPos = (short)(currentPage.getNKeys()-0);
findFirstPosInPage();
}
@Override
public boolean hasNext() {
checkValidity();
return hasValue;
}
private void goToNextPage() {
releasePage(currentPage);
IteratorPos ip = stack.remove(stack.size()-1);
currentPage = ip.page;
currentPos = ip.pos;
currentPos--;
while (currentPos < 0) {
releasePage(currentPage);
if (stack.isEmpty()) {
close();
return;// false;
}
ip = stack.remove(stack.size()-1);
currentPage = ip.page;
currentPos = ip.pos;
currentPos--;
}
while (!currentPage.isLeaf) {
//we are not on the first page here, so we can assume that pos=0 is correct to
//start with
//read last page
stack.add(new IteratorPos(currentPage, currentPos));
currentPage = (LLIndexPage) findPage(currentPage, currentPos);
currentPos = currentPage.getNKeys();
}
//leaf page positions are smaller than inner-page positions
currentPos--;
}
private boolean goToFirstPage() {
while (!currentPage.isLeaf) {
//the following is only for the initial search.
//The stored value[i] is the min-values of the according page[i+1}
int pos = currentPage.binarySearch(0, currentPos, maxKey, Long.MAX_VALUE);
if (currentPage.getNKeys() == -1) {
return false;
}
if (pos >=0) {
pos++;
} else {
pos = -(pos+1);
}
currentPos = (short) pos;
//read page
//Unlike the ascending iterator, we don't need special non-unique stuff here
stack.add(new IteratorPos(currentPage, currentPos));
currentPage = (LLIndexPage) findPage(currentPage, currentPos);
currentPos = currentPage.getNKeys();
}
return true;
}
private void gotoPosInPage() {
//when we get here, we are on a valid page with a valid position
//(TODO check for pos after goToPage())
//we only need to check the value.
nextKey = currentPage.getKeys()[currentPos];
nextValue = currentPage.getValues()[currentPos];
hasValue = true;
currentPos--;
//now progress to next element
//first progress to next page, if necessary.
if (currentPos < 0) {
goToNextPage();
if (currentPage == null) {
return;
}
}
//check for invalid value
if (currentPage.getKeys()[currentPos] < minKey) {
close();
}
}
private void findFirstPosInPage() {
//find first page
if (!goToFirstPage()) {
close();
return;
}
//find very first element.
int pos = (short) currentPage.binarySearch(0, currentPage.getNKeys(), maxKey, Long.MAX_VALUE);
if (pos < 0) {
pos = -(pos+2); //-(pos+1);
}
currentPos = (short) pos;
//check pos
if (currentPos < 0 || currentPage.getKeys()[currentPos] < minKey) {
close();
return;
}
gotoPosInPage();
}
@Override
public LongLongIndex.LLEntry next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
LongLongIndex.LLEntry e = new LongLongIndex.LLEntry(nextKey, nextValue);
if (currentPage == null) {
hasValue = false;
} else {
gotoPosInPage();
}
return e;
}
@Override
public void remove() {
//As defined in the JDO 2.2. spec:
throw new UnsupportedOperationException();
}
/**
* This method is possibly not be called if the iterator is used in 'for ( : ext) {}'
* constructs!
*/
@Override
public void close() {
// after close() everything should throw NoSuchElementException (see 2.2. spec)
currentPage = null;
}
}