Log in Help
Print
Homereleasesgate-8.4-build5748-ALLpluginsOntologysrcgatecreoleontologyimplsesame 〉 UtilTupleQueryIterator.java
 
/*
 *  Copyright (c) 1995-2012, The University of Sheffield. See the file
 *  COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
 *
 *  This file is part of GATE (see http://gate.ac.uk/), and is free
 *  software, licenced under the GNU Library General Public License,
 *  Version 2, June 1991 (in the distribution as file licence.html,
 *  and also available at http://gate.ac.uk/gate/licence.html).
 *
 *  Johann Petrak 2009-08-13
 *
 *  $Id: UtilTupleQueryIterator.java 17551 2014-03-06 11:08:34Z johann_p $
 */
package gate.creole.ontology.impl.sesame;

import gate.creole.ontology.impl.OURIImpl;
import gate.creole.ontology.impl.OBNodeIDImpl;
import gate.creole.ontology.GateOntologyException;
import gate.creole.ontology.LiteralOrONodeID;
import gate.creole.ontology.Literal;
import gate.creole.ontology.ONodeID;
import gate.creole.ontology.OConstants;
import gate.creole.ontology.OntologyTupleQuery;
import gate.creole.ontology.impl.LiteralOrONodeIDImpl;
import gate.util.GateRuntimeException;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import org.apache.log4j.Logger;
import org.openrdf.model.BNode;
import org.openrdf.model.URI;
import org.openrdf.model.Value;
import org.openrdf.query.BindingSet;
import org.openrdf.query.QueryEvaluationException;
import org.openrdf.query.QueryLanguage;
import org.openrdf.query.TupleQuery;
import org.openrdf.query.TupleQueryResult;
import org.openrdf.repository.RepositoryConnection;

/**
 * A class representing a Sesame Query. This class makes it easy to
 * iterate over query results, reuse a query with different variable
 * bindings, and provides special methods for queries where only one
 * column is retrieved.
 * <p>
 * The following steps should be carried out when using this class:
 * <ol>
 * <li>Create an object and pass on the repository connection, query string,
 * and query language constant
 * <li>Set any variables for the query using {@link #setBinding} one or more times
 * <li>Evaluate the query using {@link evaluate}. This is optional, if not done,
 * it will be done implicitly in the first call to {@link #hasNext}.
 * <li>Retrieve query results one by one using one of the next... methods
 * <li>If no more results are needed the query MUST BE CLOSED using the
 * {@link #close} function to free the query resources. However, if all
 * results have been returned and {@link #hasNext()} has returned false,
 * the query has been closed automatically. It is allowed to call close
 * on a closed query.
 * <li>To reuse the query, repeat from step 2
 * </ol>
 *
 * @author Johann Petrak
 */
 public class UtilTupleQueryIterator implements OntologyTupleQuery {

    private RepositoryConnection mRepositoryConnection;
    private SesameManager mSesameManager;
    private String mQuery;
    private TupleQueryResult mResult;
    private TupleQuery mTupleQuery;
    private ArrayList<String> mVarnames;
    private QueryLanguage mLang = QueryLanguage.SERQL;
    private boolean mIsClosed = true;
    private boolean mIsPrepared = false;
    private boolean mIsEvaluated = false;
    private Logger logger;

    public UtilTupleQueryIterator(
      SesameManager sm,
      String query, 
      OConstants.QueryLanguage queryLanguage) 
    {
      mSesameManager = sm;
      if(queryLanguage == OConstants.QueryLanguage.SPARQL) {
        mLang = QueryLanguage.SPARQL;
      } else if(queryLanguage == OConstants.QueryLanguage.SERQL) {
        mLang = QueryLanguage.SERQL;
      } else {
        throw new GateOntologyException("Query language must be SPARQL or SERQL, got "+queryLanguage);
      }
      //System.out.println("Created tuple query object: "+query);
      logger = Logger.getLogger(this.getClass().getName());
      mRepositoryConnection = sm.getRepositoryConnection();
      logger.debug("Creating query: " + query);
      mQuery = query;
      prepare();

    }

    public void prepare() {
      try {
        mTupleQuery = mRepositoryConnection.prepareTupleQuery(mLang, mQuery);
      } catch (Exception e) {
        throw new GateOntologyException("Cannot prepare tuple query: "+mQuery, e);
      }
      mIsPrepared = true;
    }

    /**
     *
     * @param name
     * @param value
     */
    public void setBinding(String name, Value value) {
      mTupleQuery.setBinding(name, value);
    }
    public void setBinding(String name, LiteralOrONodeID value) {
      mTupleQuery.setBinding(name, mSesameManager.convertLiteralOrONodeID2SesameValue(value));
    }
    public void setBinding(String name, Literal value) {
      mTupleQuery.setBinding(name, mSesameManager.toSesameLiteral(value));
    }
    public void setBinding(String name, ONodeID value) {
      mTupleQuery.setBinding(name, mSesameManager.toSesameResource(value));
    }

    /**
     * 
     */
    public void evaluate() {
      close();
      if (!mIsPrepared) {
        prepare();
      }
      try {
          mResult = mTupleQuery.evaluate();
        mIsClosed = false;
        List<String> varnamesList = mResult.getBindingNames();
        mVarnames = new ArrayList<String>(varnamesList);
      } catch (Exception e) {
        throw new GateOntologyException("Cannot evaluate queyr: "+mQuery, e);
      }
      mIsEvaluated = true;
    }

    public boolean hasNext() {
      if (!mIsPrepared) {
        prepare();
      }
      if (!mIsEvaluated) {
        evaluate();
      }
      try {
        boolean hasnext = mResult.hasNext();
        if(!hasnext) {
          close();
        }
        return hasnext;
      } catch (QueryEvaluationException e) {
        throw new GateOntologyException("Error checking for next result of query", e);
      }
    }

    /**
     * Returns the first variable of the next result row as a String.
     * If the variable is unbound it is returned as an empty String.
     * 
     * @return 
     */
    public String nextFirstAsString() {
      Value ret = nextFirstAsValue();
      if(ret == null) {
        return "";
      } else {
        return ret.stringValue();
      }
    }

    public LiteralOrONodeID nextFirst() {
      Value v = nextFirstAsValue();
      if(v instanceof BNode) {
        return new LiteralOrONodeIDImpl(new OBNodeIDImpl(v.stringValue()));
      } else if(v instanceof org.openrdf.model.Literal) {
        return new LiteralOrONodeIDImpl(
            mSesameManager.convertSesameLiteral2Literal((org.openrdf.model.Literal)v));
      } else if(v instanceof org.openrdf.model.URI) {
        URI u = (URI)v;
        // TODO: check if we want toString or stringValue() here
        return new LiteralOrONodeIDImpl(new OURIImpl(u.stringValue()));
      }
      return null;
    }


    public Value nextFirstAsValue() {
      if (mResult == null) {
        throw new GateOntologyException("No prepared query available");
      }
      Value ret;
      try {
        BindingSet bindingSet = mResult.next();
        ret = bindingSet.getValue(mVarnames.get(0));
      } catch (QueryEvaluationException e) {
        throw new GateOntologyException("Could not get next query result", e);
      }
      //logger.debug("QV: "+ret);
      return ret;
    }

    public Vector<Value> nextAsValue() {
      if(!hasNext()) {
        throw new GateOntologyException("No more query results but next was called");
      }
      Vector<Value> result = new Vector<Value>();
      try {
        BindingSet bindingSet = mResult.next();
        for (String bindingName : mVarnames) {
          Value value = bindingSet.getValue(bindingName);
          result.add(value);
        }
      } catch (QueryEvaluationException e) {
        throw new GateOntologyException("Could not get next query result", e);
      }
      logger.debug("Qvec: " + result);
      return result;

    }

   // if there are optional values the position in the vector that is not 
   // bound will return the empty string!
   public Vector<String> nextAsString() {
     if (!hasNext()) {
       throw new GateOntologyException("No more query results but next was called");
     }
     Vector<String> result = new Vector<String>();
     try {
       BindingSet bindingSet = mResult.next();
       for (String bindingName : mVarnames) {
         Value value = bindingSet.getValue(bindingName);
         /* this is not necessarily an error because a binding can be null if
          * it was part of an OPTIONAL clause
          * In that case, null will be added to the return vector.
         if(value == null) {
           throw new GateOntologyException("Could not get binding for name "+
                   bindingName+
                   " for binding names "+bindingSet.getBindingNames()+
                   " for qeury "+mQuery);
         }
          * 
          */
         if(value != null) {
           String val = value.stringValue();
           result.add(val);
         } else {
           result.add("");           
         }
       }
     } catch (QueryEvaluationException e) {
       throw new GateOntologyException("Could not get next query result", e);
     }
     return result;
   }

   
   /**
    * Returns a vector of LiterOrONodeID object that represent a result row
    * from the query. If a variable cannot be bound, the corresponding element
    * in the vector is null.
    * 
    * @return 
    */
    public Vector<LiteralOrONodeID> next() {
      if(!hasNext()) {
        throw new GateOntologyException("No more query results but next was called");
      }
      Vector<LiteralOrONodeID> result = new Vector<LiteralOrONodeID>();
      try { 
        BindingSet bindingSet = mResult.next();
        for (String bindingName : mVarnames) {
          Value value = bindingSet.getValue(bindingName);
          if(value instanceof BNode) {
            result.add(new LiteralOrONodeIDImpl(new OBNodeIDImpl(value.stringValue())));
          } else if(value instanceof org.openrdf.model.Literal) {
             result.add(new LiteralOrONodeIDImpl(
                 mSesameManager.convertSesameLiteral2Literal((org.openrdf.model.Literal)value)));
          } else if(value instanceof org.openrdf.model.URI) {
             URI u = (URI)value;
             result.add(new LiteralOrONodeIDImpl(new OURIImpl(u.stringValue())));
          } else {
            // none of the above, this MUST be null, representing an anbound
            // projection variable
            if(value != null) {
              throw new GateOntologyException("Odd result from SPARQL query: "+value+
                " Query was: "+mQuery);
            }
            result.add(null);
          }
        }
      } catch (QueryEvaluationException e) {
        throw new GateOntologyException("Could not get next query result", e);
      }
      logger.debug("Qvec: " + result);
      return result;

    }



    public void close() {
      try {
        if (!mIsClosed) {
          mResult.close();
          mIsEvaluated = false;
          mIsClosed = true;
        }
      } catch (Exception e) {
        throw new GateOntologyException("Error closing query " + mQuery, e);
      }
    }

    public void remove() {
      throw new UnsupportedOperationException("remove method not supported");
    }
    
    public List<String> getBindingNames() {
      if(!mIsPrepared) {
        throw new GateOntologyException("Cannot get bindings for tuple query before it is prepared");
      } else if(!mIsEvaluated) {
        throw new GateOntologyException("Cannot get bindings for tuple query before it is evaluated");
      } else if(mIsClosed) {
        throw new GateOntologyException("Cannot get bindings for a closed tuple query");
      } else {
        List<String> bns = null;
        try {
          bns = mResult.getBindingNames();
        } catch(QueryEvaluationException ex) {
          throw new GateOntologyException("Could not get the bindings for query "+mQuery,ex);
        }
        return bns;
      }
    }
    
    public void setIncludeInferred(boolean value) {
      mTupleQuery.setIncludeInferred(value);
    }
    
    public boolean getIncludeInferred() {
      return mTupleQuery.getIncludeInferred();
    }
    
    
  }