/*
* This file is part of the HyperGraphDB source distribution. This is copyrighted
* software. For permitted uses, licensing options and redistribution, please see
* the LicensingInformation file at the root level of the distribution.
*
* Copyright (c) 2005-2010 Kobrix Software, Inc. All rights reserved.
*/
package org.hypergraphdb.query.impl;
import java.util.NoSuchElementException;
import org.hypergraphdb.HGSearchResult;
/**
*
* <p>
* A piped query result takes the output of a query, in the form of
* a <code>HGSearchResult</code> instance and uses it as input to a "pipe"
* query.
* </p>
*
* <p>
* Such a result may be used to implement various query operators (e.g. union,
* intersection) when one of the operands is an indexed set and can be queried
* directly by a key.
* </p>
* @author Borislav Iordanov
*/
public class PipedResult<Key, Value> implements HGSearchResult<Value>
{
private HGSearchResult<Key> in;
private KeyBasedQuery<Key, Value> pipe;
private HGSearchResult<Value> currentPiped = null,
nextPiped = null,
previousPiped = null;
private boolean own_in;
/**
*
* @param in The input query result, can't be <code>null</code>.
* @param pipe The pipe query, can't be <code>null</code>.
* @param own_in Specifies whether to close the input result object when this object is closed.
*/
public PipedResult(HGSearchResult<Key> in, KeyBasedQuery<Key, Value> pipe, boolean own_in)
{
this.in = in;
this.pipe = pipe;
this.own_in = own_in;
if (in.hasNext())
{
pipe.setKey(in.next());
currentPiped = pipe.execute();
}
}
public Value current()
{
if (currentPiped == null)
throw new NoSuchElementException();
else
return currentPiped.current();
}
public void close()
{
if (currentPiped != null)
{
currentPiped.close();
currentPiped = null;
}
if (nextPiped != null)
{
nextPiped.close();
nextPiped = null;
}
if (previousPiped != null)
{
previousPiped.close();
previousPiped = null;
}
if (own_in && in != null )
in.close();
}
public boolean hasPrev()
{
if (currentPiped == null)
return false;
else if (currentPiped.hasPrev())
return true;
else if (previousPiped != null)
return previousPiped.hasPrev();
else if (!in.hasPrev())
return false;
else while (true)
{
// TODO - This is not the optimal way to do things,
// we're relying on the fact that an application will
// not be scanning back and forth frentically, but that it
// will only occasionally need to look to a prev element.
// The problem is that when we execute the pipe query, we need
// to scan to the end of the result set so as to maintain linearity
// in the overall "PipedResult".
// A more intelligent buffering of previous result sets need
// to be implemented if this proves to be a bottleneck (e.g.
// have a static variable for buffer size and grow it as the need
// arises). Or perhaps we should simply forbid bidirectionality in those
// results simply because it cannot be implemented efficiently....
pipe.setKey(in.prev());
previousPiped = pipe.execute();
if (previousPiped.hasNext())
{
do { previousPiped.next(); } while (previousPiped.hasNext());
return true;
}
else
{
previousPiped.close();
previousPiped = null;
if (!in.hasPrev())
return false;
}
}
}
public Value prev()
{
if (!hasPrev())
throw new NoSuchElementException();
else if (currentPiped.hasPrev())
return currentPiped.prev();
else
{
if (nextPiped != null)
nextPiped.close();
nextPiped = currentPiped;
currentPiped = previousPiped;
previousPiped = null;
return currentPiped.prev();
}
}
public void remove()
{
throw new UnsupportedOperationException("PipedResult.remove");
}
public boolean hasNext()
{
if (currentPiped == null)
return false;
else if (currentPiped.hasNext())
return true;
else if (nextPiped != null)
return nextPiped.hasNext();
else if (!in.hasNext())
return false;
else while (true)
{
pipe.setKey(in.next());
nextPiped = pipe.execute();
if (nextPiped.hasNext())
return true;
else
{
nextPiped.close();
nextPiped = null;
if (!in.hasNext())
return false;
}
}
}
public Value next()
{
if (!hasNext())
throw new NoSuchElementException();
if (currentPiped.hasNext())
return currentPiped.next();
else
{
if (previousPiped != null)
previousPiped.close();
previousPiped = currentPiped;
currentPiped = nextPiped;
nextPiped = null;
return currentPiped.next();
}
}
public boolean isOrdered()
{
return false;
}
}