/*
* $Id$
*
* This file is part of the OpenLink Software Virtuoso Open-Source (VOS)
* project.
*
* Copyright (C) 1998-2012 OpenLink Software
*
* This project 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; only version 2 of the License, dated June 1991.
*
* 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.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
package virtuoso.jena.driver;
import org.apache.jena.atlas.io.IndentedWriter;
import com.hp.hpl.jena.graph.Node;
import com.hp.hpl.jena.query.Query;
import com.hp.hpl.jena.shared.JenaException;
import com.hp.hpl.jena.sparql.ARQInternalErrorException;
import com.hp.hpl.jena.sparql.algebra.Op;
import com.hp.hpl.jena.sparql.algebra.Transform;
import com.hp.hpl.jena.sparql.algebra.TransformCopy;
import com.hp.hpl.jena.sparql.algebra.Transformer;
import com.hp.hpl.jena.sparql.algebra.op.OpBGP;
import com.hp.hpl.jena.sparql.core.DatasetGraph;
import com.hp.hpl.jena.sparql.core.Var;
import com.hp.hpl.jena.sparql.engine.Plan;
import com.hp.hpl.jena.sparql.engine.QueryEngineFactory;
import com.hp.hpl.jena.sparql.engine.QueryEngineRegistry;
import com.hp.hpl.jena.sparql.engine.QueryIterator;
import com.hp.hpl.jena.sparql.engine.binding.Binding;
import com.hp.hpl.jena.sparql.engine.binding.BindingHashMap;
import com.hp.hpl.jena.sparql.engine.binding.BindingMap;
import com.hp.hpl.jena.sparql.engine.iterator.QueryIteratorBase;
import com.hp.hpl.jena.sparql.engine.main.QueryEngineMain;
import com.hp.hpl.jena.sparql.serializer.SerializationContext;
import com.hp.hpl.jena.sparql.util.Context;
import com.hp.hpl.jena.sparql.util.Utils;
public class VirtuosoQueryEngine extends QueryEngineMain {
private Query eQuery = null;
public VirtuosoQueryEngine(Query query, DatasetGraph dataset,
Binding initial, Context context) {
super(query, dataset, initial, context);
eQuery = query;
}
public VirtuosoQueryEngine(Query query, DatasetGraph dataset) {
this(query, dataset, null, null);
}
@Override
public QueryIterator eval(Op op, DatasetGraph dsg, Binding initial,
Context context) {
// Extension point: access possible to all the parameters for execution.
// Be careful to deal with initial bindings.
Transform transform = new VirtTransform();
op = Transformer.transform(transform, op);
VirtGraph vg = (VirtGraph) dsg.getDefaultGraph();
String query = fixQuery(eQuery.toString(), initial, vg);
try {
java.sql.Statement stmt = vg.createStatement();
java.sql.ResultSet rs = stmt.executeQuery(query);
return (QueryIterator) new VQueryIterator(vg, rs);
} catch (Exception e) {
throw new JenaException("Can not create QueryIterator.:" + e);
}
}
private String substBindings(String query, Binding args) {
if (args == null)
return query;
StringBuffer buf = new StringBuffer();
String delim = " ,)(;.";
int i = 0;
char ch;
int qlen = query.length();
while (i < qlen) {
ch = query.charAt(i++);
if (ch == '\\') {
buf.append(ch);
if (i < qlen)
buf.append(query.charAt(i++));
} else if (ch == '"' || ch == '\'') {
char end = ch;
buf.append(ch);
while (i < qlen) {
ch = query.charAt(i++);
buf.append(ch);
if (ch == end)
break;
}
} else if (ch == '?') { // Parameter
String varData = null;
int j = i;
while (j < qlen && delim.indexOf(query.charAt(j)) < 0)
j++;
if (j != i) {
String varName = query.substring(i, j);
Node val = args.get(Var.alloc(varName));
if (val != null) {
varData = VirtGraph.Node2Str(val);
i = j;
}
}
if (varData != null)
buf.append(varData);
else
buf.append(ch);
} else {
buf.append(ch);
}
}
return buf.toString();
}
private String fixQuery(String query, Binding args, VirtGraph vg) {
StringBuffer sb = new StringBuffer("sparql\n ");
if (vg.getRuleSet() != null)
sb.append(" define input:inference '" + vg.getRuleSet() + "'\n ");
if (vg.getSameAs())
sb.append(" define input:same-as \"yes\"\n ");
if (!vg.getReadFromAllGraphs())
sb.append(" define input:default-graph-uri <" + vg.getGraphName()
+ "> \n");
sb.append(substBindings(query, args));
return sb.toString();
}
@Override
protected Op modifyOp(Op op) {
// Extension point: possible place to alter the algebra expression.
// Alternative to eval().
op = super.modifyOp(op);
return op;
}
// ---- Registration of the factory for this query engine class.
// Query engine factory.
// Call VirtQueryEngine.register() to add to the global query engine
// registry.
static QueryEngineFactory factory = new VirtQueryEngineFactory();
static public QueryEngineFactory getFactory() {
return factory;
}
static public void register() {
QueryEngineRegistry.addFactory(factory);
}
static public void unregister() {
QueryEngineRegistry.removeFactory(factory);
}
private class VirtTransform extends TransformCopy {
// Example, do nothing tranform.
@Override
public Op transform(OpBGP opBGP) {
return opBGP;
}
}
private static class VirtQueryEngineFactory implements QueryEngineFactory {
// Accept any dataset for query execution
public boolean accept(Query query, DatasetGraph dataset, Context context) {
if (dataset instanceof VirtDataSource.VirtDataSetGraph)
return true;
if (dataset.getDefaultGraph() instanceof VirtGraph)
return true;
return false;
}
public Plan create(Query query, DatasetGraph dataset, Binding initial,
Context context) {
if (!(dataset instanceof VirtDataSource.VirtDataSetGraph)) {
if (!(dataset.getDefaultGraph() instanceof VirtGraph))
throw new ARQInternalErrorException(
"VirtQueryEngineFactory: only factory VirtuosoDatasetGraph is supported");
}
// Create a query engine instance.
VirtuosoQueryEngine engine = new VirtuosoQueryEngine(query,
dataset, initial, context);
return engine.getPlan();
}
public boolean accept(Op op, DatasetGraph dataset, Context context) { // Refuse
// to
// accept
// algebra
// expressions
// directly.
return false;
}
public Plan create(Op op, DatasetGraph dataset, Binding inputBinding,
Context context) { // Shodul notbe called because acceept/Op is
// false
throw new ARQInternalErrorException(
"VirtQueryEngineFactory: factory calleddirectly with an algebra expression");
}
}
protected class VQueryIterator extends QueryIteratorBase {
java.sql.ResultSetMetaData rsmd;
java.sql.ResultSet rs;
VirtGraph vg;
boolean v_finished = false;
boolean v_prefetched = false;
BindingMap v_row;
String virt_graph = null;
protected VQueryIterator(VirtGraph _g, java.sql.ResultSet _rs) {
rs = _rs;
vg = _g;
virt_graph = vg.getGraphName();
try {
rsmd = rs.getMetaData();
} catch (Exception e) {
throw new JenaException("VQueryIterator is FAILED.:" + e);
}
}
public void output(IndentedWriter out, SerializationContext sCxt) {
out.print(Utils.className(this));
}
protected boolean hasNextBinding() {
if (!v_finished && !v_prefetched)
moveForward();
return !v_finished;
}
protected Binding moveToNextBinding() {
if (!v_finished && !v_prefetched)
moveForward();
v_prefetched = false;
if (v_finished)
return null;
return v_row;
}
protected void closeIterator() {
if (!v_finished) {
if (rs != null) {
try {
rs.close();
rs = null;
} catch (Exception e) {
}
}
}
v_finished = true;
}
protected void moveForward() throws JenaException {
try {
if (!v_finished && rs.next()) {
extractRow();
v_prefetched = true;
} else
closeIterator();
} catch (Exception e) {
throw new JenaException("Convert results are FAILED.:" + e);
}
}
protected void extractRow() throws Exception {
v_row = new BindingHashMap();
try {
for (int i = 1; i <= rsmd.getColumnCount(); i++) {
Node n = VirtGraph.Object2Node(rs.getObject(i));
if (n != null)
v_row.add(Var.alloc(rsmd.getColumnLabel(i)), n);
}
if (virt_graph != null && !virt_graph.equals("virt:DEFAULT"))
v_row.add(Var.alloc("graph"), Node.createURI(virt_graph));
} catch (Exception e) {
throw new JenaException("extractRow is FAILED.:" + e);
}
}
protected void finalize() throws Throwable {
if (!v_finished)
try {
close();
} catch (Exception e) {
}
}
@Override
protected void requestCancel() {
// TODO Auto-generated method stub
}
}
}