/*
Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved.
Contact:
SYSTAP, LLC DBA Blazegraph
2501 Calvert ST NW #106
Washington, DC 20008
licenses@blazegraph.com
This program 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; version 2 of the License.
This program 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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Created on Aug 5, 2008
*/
package com.bigdata.btree.filter;
import java.util.Iterator;
import java.util.NoSuchElementException;
import com.bigdata.btree.AbstractTuple;
import com.bigdata.btree.ITuple;
import com.bigdata.btree.ITupleCursor;
import com.bigdata.btree.ITupleIterator;
import com.bigdata.btree.ITupleSerializer;
import cutthecrap.utils.striterators.FilterBase;
/**
* Lookahead filter for an {@link ITuple}. You can push back a single
* {@link ITuple} onto the filter.
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
* @version $Id$
*
* @todo unit tests.
*/
public class LookaheadTupleFilter<E> extends FilterBase implements ITupleFilter<E> {
private static final long serialVersionUID = -5551926378159692251L;
public LookaheadTupleFilter() {
}
/**
* Extends iterator semantics for {@link ITuple} pushback.
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
* @version $Id$
* @param <E>
*/
public interface ILookaheadTupleIterator<E> extends ITupleIterator<E> {
/**
* Pushback the last visited tuple. It will be returned by
* {@link ITupleIterator#next()}.
*
* @throws IllegalStateException
* if no tuple has been read.
* @throws IllegalStateException
* if the pushback limit has been exceeded.
*/
void pushback();
}
@SuppressWarnings("unchecked")
@Override
public ILookaheadTupleIterator<E> filterOnce(Iterator src, Object context) {
if(src instanceof ITupleCursor) {
return new LookaheadTupleCursor((ITupleCursor)src, context);
}
return new LookaheadTupleIterator((ITupleIterator)src, context);
}
/**
* Implementation based on an underlying {@link ITupleCursor}.
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
* @version $Id$
* @param <E>
*/
static private class LookaheadTupleCursor<E> implements ILookaheadTupleIterator<E> {
private final ITupleCursor<E> src;
private final Object context;
private boolean pushbackAllowed = false;
public LookaheadTupleCursor(ITupleCursor<E> src, Object context) {
this.src = src;
this.context = context;
}
public void pushback() {
if (!pushbackAllowed)
throw new IllegalStateException();
src.prior();
pushbackAllowed = false;
}
public ITuple<E> next() {
final ITuple<E> tuple = src.next();
pushbackAllowed = true;
return tuple;
}
public boolean hasNext() {
return src.hasNext();
}
public void remove() {
src.remove();
}
}
/**
* Implementation based on an underlying {@link ITupleIterator}.
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
* @version $Id$
* @param <E>
*/
static private class LookaheadTupleIterator<E> implements ILookaheadTupleIterator<E> {
private final ITupleIterator<E> src;
private final Object context;
/**
* <code>true</code> iff pushback is allowed. when <code>true</code>,
* {@link #tupleBuffer} will containing a copy of the data for the
* last visited {@link ITuple}. note that we always copy the data for
* the visited tuples into {@link #tupleBuffer}. pushback itself
* consists of clearing this flag.
*/
private boolean pushbackAllowed = false;
/**
* when non-<code>null</code> and {@link #pushbackAllowed} is
* <code>false</code>, then this buffer contains the next tuple to be
* read.
*/
private AbstractTuple<E> tupleBuffer;
/**
* @param src
*/
public LookaheadTupleIterator(ITupleIterator<E> src, Object context) {
this.src = src;
this.context = context;
}
public void pushback() {
if (!pushbackAllowed)
throw new IllegalStateException();
pushbackAllowed = false;
}
public ITuple<E> next() {
if (!hasNext())
throw new NoSuchElementException();
if(!pushbackAllowed && tupleBuffer != null) {
// return the buffered tuple.
pushbackAllowed = true;
return tupleBuffer;
}
final ITuple<E> tuple = src.next();
if (tupleBuffer == null) {
tupleBuffer = new AbstractTuple<E>(tuple.flags()){
public int getSourceIndex() {
return tuple.getSourceIndex();
}
public ITupleSerializer getTupleSerializer() {
return tuple.getTupleSerializer();
}
};
}
// copy the next tuple's data into the buffer.
tupleBuffer.copyTuple(tuple);
// pushback of this tuple is allowed.
pushbackAllowed = true;
// return the buffered tuple.
return tupleBuffer;
}
public boolean hasNext() {
if (!pushbackAllowed && tupleBuffer != null) {
// there is a buffered tuple to return.
return true;
}
// the source iterator will visit another tuple.
return src.hasNext();
}
public void remove() {
throw new UnsupportedOperationException();
}
}
}