package com.bigdata.rdf.store;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import org.apache.log4j.Logger;
import org.openrdf.model.Value;
import com.bigdata.bop.Constant;
import com.bigdata.bop.IBindingSet;
import com.bigdata.bop.IConstant;
import com.bigdata.bop.IVariable;
import com.bigdata.rdf.internal.IV;
import com.bigdata.rdf.model.BigdataValue;
import com.bigdata.relation.accesspath.BlockingBuffer;
import com.bigdata.relation.rule.eval.ISolution;
import com.bigdata.striterator.AbstractChunkedResolverator;
import com.bigdata.striterator.IChunkedOrderedIterator;
/**
* Efficiently resolve term identifiers in Bigdata {@link ISolution}s to RDF
* {@link BigdataValue}s.
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
* @version $Id$
*/
public class BigdataSolutionResolverator
extends
AbstractChunkedResolverator<ISolution, IBindingSet, AbstractTripleStore> {
final private static Logger log = Logger
.getLogger(BigdataSolutionResolverator.class);
/**
*
* @param db
* Used to resolve term identifiers to {@link Value} objects.
* @param src
* The source iterator (will be closed when this iterator is
* closed).
*
* FIXME must accept reverse bnodes map (from term identifier to
* blank nodes) for resolution of blank nodes within a Sesame
* connection context.
*/
public BigdataSolutionResolverator(final AbstractTripleStore db,
final IChunkedOrderedIterator<ISolution> src) {
super(db, src, new BlockingBuffer<IBindingSet[]>(
db.getChunkOfChunksCapacity(),
db.getChunkCapacity(),
db.getChunkTimeout(),
TimeUnit.MILLISECONDS));
}
/**
* Strengthens the return type.
*/
public BigdataSolutionResolverator start(ExecutorService service) {
return (BigdataSolutionResolverator) super.start(service);
}
/**
* Resolve a chunk of {@link ISolution}s into a chunk of
* {@link IBindingSet}s in which term identifiers have been resolved to
* {@link BigdataValue}s.
*/
protected IBindingSet[] resolveChunk(final ISolution[] chunk) {
if (log.isInfoEnabled())
log.info("Fetched chunk: size=" + chunk.length);
/*
* Create a collection of the distinct term identifiers used in this
* chunk.
*/
final Collection<IV<?, ?>> ids = new HashSet<IV<?, ?>>(chunk.length
* state.getSPOKeyArity());
for (ISolution solution : chunk) {
final IBindingSet bindingSet = solution.getBindingSet();
assert bindingSet != null;
final Iterator<Map.Entry<IVariable, IConstant>> itr = bindingSet
.iterator();
while (itr.hasNext()) {
final Map.Entry<IVariable, IConstant> entry = itr.next();
final IV<?,?> iv = (IV<?,?>) entry.getValue().get();
if (iv == null) {
throw new RuntimeException("NULL? : var=" + entry.getKey()
+ ", " + bindingSet);
}
ids.add(iv);
}
}
if (log.isInfoEnabled())
log.info("Resolving " + ids.size() + " term identifiers");
// batch resolve term identifiers to terms.
final Map<IV<?,?>, BigdataValue> terms = state.getLexiconRelation()
.getTerms(ids);
/*
* Assemble a chunk of resolved elements.
*/
{
final IBindingSet[] chunk2 = new IBindingSet[chunk.length];
int i = 0;
for (ISolution e : chunk) {
final IBindingSet f = getBindingSet(e, terms);
chunk2[i++] = f;
}
// return the chunk of resolved elements.
return chunk2;
}
}
/**
* Resolve the term identifiers in the {@link ISolution} using the map
* populated when we fetched the current chunk and return the
* {@link IBindingSet} for that solution in which term identifiers have been
* resolved to their corresponding {@link BigdataValue}s.
*
* @param solution
* A solution whose {@link Long}s will be interpreted as term
* identifiers and resolved to the corresponding
* {@link BigdataValue}s.
*
* @return The corresponding {@link IBindingSet} in which the term
* identifiers have been resolved to {@link BigdataValue}s.
*
* @throws IllegalStateException
* if the {@link IBindingSet} was not materialized with the
* {@link ISolution}.
*/
private IBindingSet getBindingSet(final ISolution solution,
final Map<IV<?,?>, BigdataValue> terms) {
if (solution == null)
throw new IllegalArgumentException();
if (terms == null)
throw new IllegalArgumentException();
final IBindingSet bindingSet = solution.getBindingSet();
if(bindingSet == null) {
throw new IllegalStateException("BindingSet was not materialized");
}
final Iterator<Map.Entry<IVariable, IConstant>> itr = bindingSet
.iterator();
while (itr.hasNext()) {
final Map.Entry<IVariable, IConstant> entry = itr.next();
final Object boundValue = entry.getValue().get();
if (!(boundValue instanceof IV<?, ?>)) {
continue;
}
final IV<?,?> iv = (IV<?,?>) boundValue;
final BigdataValue value = terms.get(iv);
if (value == null) {
throw new RuntimeException("Could not resolve termId="
+ iv);
}
/*
* Replace the binding.
*
* FIXME This probably needs to strip out the BigdataSail#NULL_GRAPH
* since that should not become bound.
*/
bindingSet.set(entry.getKey(), new Constant<BigdataValue>(
value));
}
return bindingSet;
}
}