/*
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 7, 2008
*/
package com.bigdata.striterator;
import java.util.Arrays;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* Chunk-at-a-time filter.
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
* @version $Id$
* @param <I>
* The parameterized generic type of the source iterator.
* @param <E>
* The generic type of the elements visited by the source iterator.
* @param <F>
* The generic type of the elements after applying this filter. This
* will differ from {@link E} iff the filter is transforming the
* element type.
*
* @todo This class is redundant with the {@link ChunkedConvertingIterator}
*/
abstract public class ChunkedFilter<I extends IChunkedIterator<E>, E, F>
implements IFilter<I, E, F> {
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* Caller's object.
*/
protected Object state;
/**
* The natural order of the filtered chunks. This field is set iff specified
* explicitly to the ctor and is otherwise <code>null</code>. When
* <code>null</code> the filtered iterator is <strong>assumed to be
* unordered</strong>. This assumption is made since chunk-at-a-time
* transforms can change the natural order of the chunk in many, many
* different ways, e.g., by changing the type of the elements in the chunk.
*/
final protected IKeyOrder<F> keyOrder;
public ChunkedFilter() {
this(null/* state */, null/* keyOrder */);
}
public ChunkedFilter(final Object state) {
this(state, null/* keyOrder */);
}
/**
*
* @param state
* Application state (optional).
* @param keyOrder
* The natural sort order for the filtered iterator (optional).
*/
public ChunkedFilter(final Object state, final IKeyOrder<F> keyOrder) {
this.state = state;
this.keyOrder = keyOrder;
}
/**
* {@inheritDoc}
* <p>
* Note: There was a historical compiler problem with this method when
* generics were not specified.
*
* @see <a href="https://sourceforge.net/apps/trac/bigdata/ticket/517"> Java
* 7 Compatibility </a>
*/
public IChunkedOrderedIterator<F> filter(final I src) {
return new ChunkedFilteringIterator<I, E, F>(src, this);
}
// /**
// * TODO The need for this variant is worrysome - it is required if you do
// * NOT specify the generic types and then try to use this class. It is now
// * an error with Java 7. I have taken this variant out. Hopefully it is
// * no longer required by recent Java 6 compilers....
// *
// * @see <a href="https://sourceforge.net/apps/trac/bigdata/ticket/517"> Java
// * 7 Compatibility </a>
// */
// public IChunkedOrderedIterator<F> filter(final Iterator src) {
//
// return new ChunkedFilteringIterator<I, E, F>((I) src, this);
//
// }
/**
* Process a chunk of elements, returning another chunk of elements.
*
* @param chunk
* A chunk of elements from the source iterator.
*
* @return The next chunk of elements to be visited by the filtered
* iterator. If all elements are filtered then this method should
* return either an empty chunk or <code>null</code>.
*/
abstract protected F[] filterChunk(E[] chunk);
/**
* Implementation.
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
* @version $Id$
* @param <I>
* @param <E>
* @param <F>
*/
private static class ChunkedFilteringIterator<I extends IChunkedIterator<E>, E, F>
implements IChunkedOrderedIterator<F> {
/**
* The source iterator.
*/
private final I src;
/**
* The chunk-at-a-time filter.
*/
private final ChunkedFilter<I, E, F> filter;
/**
* A chunk of filtered elements (initially <code>null</code>). If
* this chunk is <code>null</code> or is exhausted and the source
* iterator is also exhausted then the filtered iterator is exhausted.
*/
private F[] chunk = null;
/**
* The index of the next element to be visited in {@link #chunk}. This
* is initially ZERO (0). It is incremented each time {@link #next()} is
* called. The current {@link #chunk} is exhausted when {@link #index}
* equals the <code>chunk.length</code>.
*/
private int index = 0;
/**
* @param src
* @param filter
*/
public ChunkedFilteringIterator(I src, ChunkedFilter<I, E, F> filter) {
this.src = src;
this.filter = filter;
}
public boolean hasNext() {
while ((chunk == null || index == chunk.length) && src.hasNext()) {
// fetch and filter another chunk.
chunk = filter.filterChunk(src.nextChunk());
// reset the index into the current chunk.
index = 0;
}
if (chunk == null || index == chunk.length) {
// nothing left.
return false;
}
return true;
}
public F next() {
if (!hasNext())
throw new NoSuchElementException();
return chunk[index++];
}
public F[] nextChunk() {
if (!hasNext())
throw new NoSuchElementException();
if (index == 0) {
// return the entire chunk.
final F[] tmp = chunk;
chunk = null;
return tmp;
}
/*
* Make a dense copy of the rest of the chunk and return that.
*/
final int nremaining = chunk.length - index;
/*
* Dynamically instantiation an array of the same component type
* as the objects that we are visiting.
*/
final F[] tmp = (F[]) java.lang.reflect.Array.newInstance(
// chunk[index].getClass(),
chunk.getClass().getComponentType(),
nremaining);
for (int i = 0; i < nremaining; i++) {
tmp[i] = chunk[index + i];
}
// we need another chunk after this.
chunk = null;
return tmp;
}
public void remove() {
throw new UnsupportedOperationException();
}
public void close() {
src.close();
}
public IKeyOrder<F> getKeyOrder() {
return filter.keyOrder;
}
public F[] nextChunk(IKeyOrder<F> keyOrder) {
if (keyOrder == null)
throw new IllegalArgumentException();
final F[] chunk = nextChunk();
if (!keyOrder.equals(getKeyOrder())) {
// sort into the required order.
Arrays.sort(chunk, 0, chunk.length, keyOrder.getComparator());
}
return chunk;
}
}
}