/**
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 Mar 1, 2012
*/
package com.bigdata.rdf.sparql.ast.service;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.openrdf.model.URI;
import org.openrdf.query.Binding;
import org.openrdf.query.BindingSet;
import org.openrdf.query.impl.MapBindingSet;
import com.bigdata.bop.Constant;
import com.bigdata.bop.IBindingSet;
import com.bigdata.bop.IConstant;
import com.bigdata.bop.IVariable;
import com.bigdata.bop.IVariableOrConstant;
import com.bigdata.bop.Var;
import com.bigdata.bop.bindingSet.ListBindingSet;
import com.bigdata.rdf.internal.IV;
import com.bigdata.rdf.internal.IVCache;
import com.bigdata.rdf.internal.NotMaterializedException;
import com.bigdata.rdf.lexicon.LexiconRelation;
import com.bigdata.rdf.model.BigdataURI;
import com.bigdata.rdf.model.BigdataValue;
import com.bigdata.rdf.sail.BigdataValueReplacer;
import com.bigdata.rdf.store.AbstractTripleStore;
/**
* Helper class for {@link ServiceCall} invocations.
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
* @version $Id$
*/
public class ServiceCallUtility {
private static final String ERR_NOT_BOUND = "Service reference variable is not bound";
private static final String ERR_NOT_URI = "Service reference does not evaluate to a URI";
private static final String ERR_NOT_MATERIALIZED = "Service reference is not materialized";
/**
* Return the effective service URI IFF the value expression for the service
* reference is a constant.
*
* @return The effective service URI IFF it is a constant and otherwise
* <code>null</code>.
*
* @throws RuntimeException
* if the service reference is a constant but does not evaluate
* to a {@link URI}.
* @throws NotMaterializedException
* if the service reference evaluates to an {@link IV} which is
* not materialized.
*/
static public BigdataURI getConstantServiceURI(
final IVariableOrConstant<?> serviceRef) {
if (serviceRef.isVar()) {
return null;
}
@SuppressWarnings("rawtypes")
final IV<?, ?> serviceRefIV = (IV) ((IConstant) serviceRef).get();
if (!serviceRefIV.isURI())
throw new RuntimeException(ERR_NOT_URI);
if (!serviceRefIV.hasValue())
throw new NotMaterializedException(ERR_NOT_MATERIALIZED);
final BigdataURI serviceURI = (BigdataURI) serviceRefIV.getValue();
return serviceURI;
}
/**
* Return the effective service URI.
*
* @param bset
* A solution which will be used to evaluate the service
* reference value expression.
*
* @return
*
* @throws RuntimeException
* if the service reference is not bound.
* @throws RuntimeException
* if the service reference does not evaluate to a {@link URI}.
* @throws NotMaterializedException
* if the service reference evaluates to an {@link IV} which is
* not materialized.
*/
static public BigdataURI getServiceURI(
final IVariableOrConstant<?> serviceRef, final IBindingSet bset) {
// Evaluate the serviceRef expression.
@SuppressWarnings("rawtypes")
final IV<?, ?> serviceRefIV = (IV) serviceRef.get(bset);
if (serviceRefIV == null)
throw new RuntimeException(ERR_NOT_BOUND);
if (!serviceRefIV.isURI())
throw new RuntimeException(ERR_NOT_URI);
if (!serviceRefIV.hasValue())
throw new NotMaterializedException(ERR_NOT_MATERIALIZED);
final BigdataURI serviceURI = (BigdataURI) serviceRefIV.getValue();
return serviceURI;
}
/**
* Convert the {@link IBindingSet} into an openrdf {@link BindingSet}.
* <p>
* Note: The {@link IVCache} MUST be set for non-inline {@link IV}s.
*
* @param vars
* The set of variables which are to be projected (optional).
* When given, only the projected variables are in the returned
* {@link BindingSet}.
* @param in
* A bigdata {@link IBindingSet} with materialized values.
*
* @throws NotMaterializedException
* if a non-inline {@link IV} has not had its {@link IVCache}
* set.
*/
static public BindingSet bigdata2Openrdf(final LexiconRelation lex,
final Set<IVariable<?>> vars, final IBindingSet in) {
final MapBindingSet out = new MapBindingSet();
@SuppressWarnings("rawtypes")
final Iterator<Map.Entry<IVariable,IConstant>> itr = in.iterator();
while(itr.hasNext()) {
@SuppressWarnings("rawtypes")
final Map.Entry<IVariable,IConstant> e = itr.next();
final IVariable<?> var = e.getKey();
if (vars != null && !vars.contains(var)) {
// This variable is not being projected.
continue;
}
final String name = var.getName();
@SuppressWarnings("rawtypes")
final IV iv = (IV) e.getValue().get();
final BigdataValue value;
if (iv.isInline()) {
/**
* Materialize inline IV as Value.
*
* @see <a
* href="http://sourceforge.net/apps/trac/bigdata/ticket/632">
* NotMaterializedException when a SERVICE call needs
* variables that are provided as query input bindings </a>
*/
value = iv.asValue(lex);
} else {
try {
// Recover Value from the IVCache.
value = iv.getValue();
} catch (NotMaterializedException ex) {
/*
* Add the variable name to the stack trace.
*/
throw new NotMaterializedException("var=" + name + ", val="
+ iv, ex);
}
}
out.addBinding(name, value);
}
return out;
}
/**
* Convert an openrdf {@link BindingSet} into a bigdata {@link IBindingSet}.
* The {@link BindingSet} MUST contain {@link BigdataValue}s and the
* {@link IV}s for those {@link BigdataValue}s MUST have been resolved
* against the database and the {@link IVCache} association set.
*
* @param vars
* The variables to be projected (optional). When given, only the
* projected variables are in the returned {@link IBindingSet}.
* @param in
* The openrdf {@link BindingSet}
*
* @return The bigdata {@link IBindingSet}.
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
static private IBindingSet openrdf2Bigdata(//
final Set<IVariable<?>> vars,//
final BindingSet in//
) {
final IBindingSet out = new ListBindingSet();
final Iterator<Binding> itr = in.iterator();
while(itr.hasNext()) {
final Binding e = itr.next();
final String name = e.getName();
final IVariable<?> var = Var.var(name);
if (vars != null && !vars.contains(var)) {
// This variable is not being projected.
continue;
}
// Note: MUST already be BigdataValues.
final BigdataValue value = (BigdataValue) e.getValue();
// Note: IVs MUST already be resolved.
final IV<?,?> iv = value.getIV();
if(iv == null)
throw new AssertionError();
// IV must have cached Value.
if (!iv.hasValue())
throw new AssertionError();
// The cached Value must be the Value (objects point at each other)
if(iv.getValue() != value)
throw new AssertionError();
out.set(var, new Constant(iv));
}
return out;
}
/**
* Convert {@link IBindingSet}[] to openrdf {@link BindingSet}[].
*
* @param projectedVars
* When given, variables which are not projected will not be
* present in the returned solutions (optional).
* @param in
* The solutions to be converted (required).
*/
static public BindingSet[] convert(final LexiconRelation lex,
final Set<IVariable<?>> projectedVars, final IBindingSet[] in) {
final BindingSet[] out = new BindingSet[in.length];
for (int i = 0; i < in.length; i++) {
out[i] = ServiceCallUtility.bigdata2Openrdf(lex, projectedVars,
in[i]);
}
return out;
}
/**
* Batch resolve BigdataValues to IVs. This is necessary in order to have
* subsequent JOINs succeed when they join on variables which are bound to
* terms which are in the lexicon.
* <p>
* Note: This will be a distributed operation on a cluster.
*/
static public IBindingSet[] resolve(final AbstractTripleStore db,
final BindingSet[] serviceResults) {
final BindingSet[] resolvedServiceResults;
{
final Object[] b = new BigdataValueReplacer(db).replaceValues(
null/* dataset */, serviceResults/* bindings */);
resolvedServiceResults = (BindingSet[]) b[1];
}
/*
* Convert the openrdf BindingSet[] into a bigdata IBindingSet[].
*/
final IBindingSet[] bigdataSolutions = new IBindingSet[resolvedServiceResults.length];
{
for (int i = 0; i < resolvedServiceResults.length; i++) {
final BindingSet bset = resolvedServiceResults[i];
final IBindingSet bset2 = openrdf2Bigdata(
null/* projectedVars */, bset);
bigdataSolutions[i] = bset2;
}
}
return bigdataSolutions;
}
}