JENA-1800: Use QueryType with exec*(queryString) operations.

Use QueryType to choose the "Accept" header.
If no known query type, use an all-purpose content negotiation string.
diff --git a/jena-rdfconnection/src/main/java/org/apache/jena/rdfconnection/RDFConnectionRemote.java b/jena-rdfconnection/src/main/java/org/apache/jena/rdfconnection/RDFConnectionRemote.java
index fa76242..7d13541 100644
--- a/jena-rdfconnection/src/main/java/org/apache/jena/rdfconnection/RDFConnectionRemote.java
+++ b/jena-rdfconnection/src/main/java/org/apache/jena/rdfconnection/RDFConnectionRemote.java
@@ -21,6 +21,7 @@
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.util.Objects;
+import java.util.function.Consumer;
 import java.util.function.Supplier;
 
 import org.apache.http.HttpEntity;
@@ -78,9 +79,10 @@
     protected final RDFFormat outputTriples;
     protected final String acceptGraph;
     protected final String acceptDataset;
-    protected final String acceptSparqlResults;
+    // All purpose SPARQL results header.
     protected final String acceptSelectResult;
     protected final String acceptAskResult;
+    protected final String acceptSparqlResults;
 
     // Whether to check SPARQL queries given as strings by parsing them.
     protected final boolean parseCheckQueries;
@@ -142,19 +144,99 @@
         return destination;
     }
 
+    // For custom content negotiation.
+
+    // This class overrides each of these to pass down the query type as well.
+    // Then we can derive the accept header if customized without needing to parse
+    // the query. This allows an arbitrary string for a query and allows the remote
+    // server to have custom syntax extensions or interpretations of comments.
+
+    /**
+     * Execute a SELECT query and process the ResultSet with the handler code.
+     * @param queryString
+     * @param resultSetAction
+     */
+    @Override
+    public void queryResultSet(String queryString, Consumer<ResultSet> resultSetAction) {
+        Txn.executeRead(this, ()->{
+            try ( QueryExecution qExec = query(queryString, QueryType.SELECT) ) {
+                ResultSet rs = qExec.execSelect();
+                resultSetAction.accept(rs);
+            }
+        } );
+    }
+
+    /**
+     * Execute a SELECT query and process the rows of the results with the handler code.
+     * @param queryString
+     * @param rowAction
+     */
+    @Override
+    public void querySelect(String queryString, Consumer<QuerySolution> rowAction) {
+        Txn.executeRead(this, ()->{
+            try ( QueryExecution qExec = query(queryString, QueryType.SELECT) ) {
+                qExec.execSelect().forEachRemaining(rowAction);
+            }
+        } );
+    }
+
+    /** Execute a CONSTRUCT query and return as a Model */
+    @Override
+    public Model queryConstruct(String queryString) {
+        return
+            Txn.calculateRead(this, ()->{
+                try ( QueryExecution qExec = query(queryString, QueryType.CONSTRUCT) ) {
+                    return qExec.execConstruct();
+                }
+            } );
+    }
+
+    /** Execute a DESCRIBE query and return as a Model */
+    @Override
+    public Model queryDescribe(String queryString) {
+        return
+            Txn.calculateRead(this, ()->{
+                try ( QueryExecution qExec = query(queryString, QueryType.DESCRIBE) ) {
+                    return qExec.execDescribe();
+                }
+            } );
+    }
+
+    /** Execute a ASK query and return a boolean */
+    @Override
+    public boolean queryAsk(String queryString) {
+        return
+            Txn.calculateRead(this, ()->{
+                try ( QueryExecution qExec = query(queryString, QueryType.ASK) ) {
+                    return qExec.execAsk();
+                }
+            } );
+    }
+
+    /**
+     * Operation that passed down the query type so the accept header can be set without parsing the query string.
+     * @param queryString
+     * @param queryType
+     * @return QueryExecution
+     */
+    protected QueryExecution query(String queryString, QueryType queryType) {
+        Objects.requireNonNull(queryString);
+        return queryExec(null, queryString, queryType);
+    }
+
     @Override
     public QueryExecution query(String queryString) {
         Objects.requireNonNull(queryString);
-        return queryExec(null, queryString);
+        return queryExec(null, queryString, null);
     }
 
     @Override
     public QueryExecution query(Query query) {
         Objects.requireNonNull(query);
-        return queryExec(query, null);
+        return queryExec(query, null, null);
     }
 
-    private QueryExecution queryExec(Query query, String queryString) {
+    private QueryExecution queryExec(Query query, String queryString, QueryType queryType) {
         checkQuery();
         if ( query == null && queryString == null )
             throw new InternalErrorException("Both query and query string are null");
@@ -164,34 +246,59 @@
         }
 
         // Use the query string as provided if possible, otherwise serialize the query.
-        String queryStringToSend = ( queryString != null ) ?  queryString : query.toString();
-        return exec(()-> createQueryExecution(query, queryStringToSend));
+        String queryStringToSend = ( queryString != null ) ? queryString : query.toString();
+        return exec(()-> createQueryExecution(query, queryStringToSend, queryType));
     }
 
     // Create the QueryExecution
-    private QueryExecution createQueryExecution(Query query, String queryStringToSend) {
+    private QueryExecution createQueryExecution(Query query, String queryStringToSend, QueryType queryType) {
         QueryExecution qExec = new QueryEngineHTTP(svcQuery, queryStringToSend, httpClient, httpContext);
         QueryEngineHTTP qEngine = (QueryEngineHTTP)qExec;
+        QueryType qt = queryType;
+        if ( query != null && qt == null )
+            qt = query.queryType();
+        if ( qt == null )
+            qt = QueryType.UNKNOWN;
         // Set the accept header - use the most specific method.
-        if ( query != null ) {
-            if ( query.isSelectType() && acceptSelectResult != null )
-                qEngine.setAcceptHeader(acceptSelectResult);
-            if ( query.isAskType() && acceptAskResult != null )
-                qEngine.setAcceptHeader(acceptAskResult);
-            if ( ( query.isConstructType() || query.isDescribeType() ) && acceptGraph != null )
-                qEngine.setAcceptHeader(acceptGraph);
-            if ( query.isConstructQuad() )
-                qEngine.setDatasetContentType(acceptDataset);
+        switch(qt) {
+            case SELECT :
+                if ( acceptSelectResult != null )
+                    qEngine.setAcceptHeader(acceptSelectResult);
+                break;
+            case ASK :
+                if ( acceptAskResult != null )
+                    qEngine.setAcceptHeader(acceptAskResult);
+                break;
+            case DESCRIBE :
+            case CONSTRUCT :
+                if ( acceptGraph != null )
+                    qEngine.setAcceptHeader(acceptGraph);
+                break;
+            case UNKNOWN:
+                // All-purpose content type.
+                if ( acceptSparqlResults != null )
+                    qEngine.setAcceptHeader(acceptSparqlResults);
+                else
+                    // No idea! Set an "anything" and hope.
+                    // (Reasonable chance this is going to end up as HTML though.)
+                    qEngine.setAcceptHeader("*/*");
+            default :
+                break;
         }
-        // Use the general one.
-        if ( qEngine.getAcceptHeader() == null && acceptSparqlResults != null )
-            qEngine.setAcceptHeader(acceptSparqlResults);
         // Make sure it was set somehow.
         if ( qEngine.getAcceptHeader() == null )
             throw new JenaConnectionException("No Accept header");
         return qExec ;
     }
 
+    private void acc(StringBuilder sBuff, String acceptString) {
+        if ( acceptString == null )
+            return;
+        if ( sBuff.length() != 0 )
+            sBuff.append(", ");
+        sBuff.append(acceptString);
+    }
+
     @Override
     public void update(String updateString) {
         Objects.requireNonNull(updateString);
@@ -427,7 +534,7 @@
         });
     }
 
-    /** Do a PUT or POST to a dataset, sending the contents of a daatsets.
+    /** Do a PUT or POST to a dataset, sending the contents of a dataset.
      * The Content-Type is {@code application/n-quads}.
      * <p>
      * "Replace" implies PUT, otherwise a POST is used.
@@ -497,13 +604,13 @@
 
     /** Create an HttpEntity for the graph. */
     protected HttpEntity graphToHttpEntity(Graph graph, RDFFormat syntax) {
-        // Length - leaves connection reusable. 
+        // Length - leaves connection reusable.
         return graphToHttpEntityWithLength(graph, syntax);
     }
-    
-    /** 
+
+    /**
      * Create an HttpEntity for the graph. The HTTP entity will have the length but this
-     * requires serialising the graph at the point when this function is called.  
+     * requires serialising the graph at the point when this function is called.
      */
     private HttpEntity graphToHttpEntityWithLength(Graph graph, RDFFormat syntax) {
         String ct = syntax.getLang().getContentType().getContentType();
@@ -535,10 +642,10 @@
 
     /** Create an HttpEntity for the dataset */
     protected HttpEntity datasetToHttpEntity(DatasetGraph dataset, RDFFormat syntax) {
-        // Length - leaves connection reusable. 
+        // Length - leaves connection reusable.
         return datasetToHttpEntityWithLength(dataset, syntax);
     }
-        
+
     private HttpEntity datasetToHttpEntityWithLength(DatasetGraph dataset, RDFFormat syntax) {
         String ct = syntax.getLang().getContentType().getContentType();
         ByteArrayOutputStream out = new ByteArrayOutputStream(128*1024);
diff --git a/jena-rdfconnection/src/test/java/org/apache/jena/rdfconnection/AbstractTestRDFConnection.java b/jena-rdfconnection/src/test/java/org/apache/jena/rdfconnection/AbstractTestRDFConnection.java
index 0482e9c..91971f3 100644
--- a/jena-rdfconnection/src/test/java/org/apache/jena/rdfconnection/AbstractTestRDFConnection.java
+++ b/jena-rdfconnection/src/test/java/org/apache/jena/rdfconnection/AbstractTestRDFConnection.java
@@ -23,12 +23,9 @@
 import org.apache.jena.atlas.iterator.Iter;
 import org.apache.jena.atlas.junit.BaseTest;
 import org.apache.jena.atlas.lib.StrUtils;
-import org.apache.jena.query.Dataset;
-import org.apache.jena.query.DatasetFactory;
-import org.apache.jena.query.ReadWrite;
+import org.apache.jena.query.*;
 import org.apache.jena.rdf.model.Model;
 import org.apache.jena.rdf.model.ModelFactory;
-import org.apache.jena.rdfconnection.RDFConnection;
 import org.apache.jena.riot.RDFDataMgr;
 import org.apache.jena.sparql.core.DatasetGraph;
 import org.apache.jena.sparql.sse.SSE;
@@ -41,10 +38,10 @@
 public abstract class AbstractTestRDFConnection extends BaseTest {
     // Testing data.
     static String DIR = "testing/RDFConnection/";
-    
+
     protected abstract RDFConnection connection();
     // Not all connection types support abort.
-    protected abstract boolean supportsAbort(); 
+    protected abstract boolean supportsAbort();
 
     // ---- Data
     static String dsgdata = StrUtils.strjoinNL
@@ -54,7 +51,7 @@
         ,"  (graph :g2 (:s :p :o) (:s2 :p2 :o))"
         ,")"
         );
-    
+
     static String dsgdata2 = StrUtils.strjoinNL
         ("(dataset"
         ,"  (graph (:x :y :z))"
@@ -62,7 +59,7 @@
         ,")"
         );
 
-    
+
     static String graph1 = StrUtils.strjoinNL
         ("(graph (:s :p :o) (:s1 :p1 :o))"
         );
@@ -70,7 +67,7 @@
     static String graph2 = StrUtils.strjoinNL
         ("(graph (:s :p :o) (:s2 :p2 :o))"
         );
-    
+
     static DatasetGraph dsg        = SSE.parseDatasetGraph(dsgdata);
     static Dataset      dataset    = DatasetFactory.wrap(dsg);
     static DatasetGraph dsg2       = SSE.parseDatasetGraph(dsgdata2);
@@ -91,9 +88,9 @@
         // Allow multiple close()
         conn.close();
     }
-    
+
     @Test public void dataset_load_1() {
-        String testDataFile = DIR+"data.trig"; 
+        String testDataFile = DIR+"data.trig";
         try ( RDFConnection conn = connection() ) {
             conn.loadDataset(testDataFile);
             Dataset ds0 = RDFDataMgr.loadDataset(testDataFile);
@@ -104,7 +101,7 @@
 
     @Test public void dataset_put_1() {
         try ( RDFConnection conn = connection() ) {
-            conn.putDataset(dataset); 
+            conn.putDataset(dataset);
             Dataset ds1 = conn.fetchDataset();
             assertTrue("Datasets not isomorphic", isomorphic(dataset, ds1));
         }
@@ -112,7 +109,7 @@
 
     @Test public void dataset_put_2() {
         try ( RDFConnection conn = connection() ) {
-            conn.putDataset(dataset); 
+            conn.putDataset(dataset);
             conn.putDataset(dataset2);
             Dataset ds1 = conn.fetchDataset();
             assertTrue("Datasets not isomorphic", isomorphic(dataset2, ds1));
@@ -126,7 +123,7 @@
             assertTrue("Datasets not isomorphic", isomorphic(dataset, ds1));
         }
     }
-    
+
     @Test public void dataset_post_2() {
         try ( RDFConnection conn = connection() ) {
             conn.loadDataset(dataset);
@@ -140,9 +137,9 @@
     }
 
     // Default graph
-    
+
     @Test public void graph_load_1() {
-        String testDataFile = DIR+"data.ttl"; 
+        String testDataFile = DIR+"data.ttl";
         Model m0 = RDFDataMgr.loadModel(testDataFile);
         try ( RDFConnection conn = connection() ) {
             conn.load(testDataFile);
@@ -153,7 +150,7 @@
 
     @Test public void graph_put_1() {
         try ( RDFConnection conn = connection() ) {
-            conn.put(model1); 
+            conn.put(model1);
             Dataset ds1 = conn.fetchDataset();
             Model m0 = conn.fetch();
             assertTrue("Models not isomorphic", isomorphic(model1, ds1.getDefaultModel()));
@@ -164,7 +161,7 @@
 
     @Test public void graph_put_2() {
         try ( RDFConnection conn = connection() ) {
-            conn.put(model1); 
+            conn.put(model1);
             conn.put(model2);
             Model m = conn.fetch();
             assertTrue("Models not isomorphic", isomorphic(m, model2));
@@ -179,7 +176,7 @@
             assertTrue("Models not isomorphic", isomorphic(m, model1));
         }
     }
-    
+
     @Test public void graph_post_2() {
         try ( RDFConnection conn = connection() ) {
             conn.load(model1);
@@ -191,11 +188,11 @@
     }
 
     // DELETE
-    
+
     // Named graphs
-    
+
     @Test public void named_graph_load_1() {
-        String testDataFile = DIR+"data.ttl"; 
+        String testDataFile = DIR+"data.ttl";
         Model m0 = RDFDataMgr.loadModel(testDataFile);
         try ( RDFConnection conn = connection() ) {
             conn.load(graphName, testDataFile);
@@ -208,7 +205,7 @@
 
     @Test public void named_graph_put_1() {
         try ( RDFConnection conn = connection() ) {
-            conn.put(graphName, model1); 
+            conn.put(graphName, model1);
             Dataset ds1 = conn.fetchDataset();
             Model m0 = conn.fetch(graphName);
             assertTrue("Models not isomorphic", isomorphic(model1, ds1.getNamedModel(graphName)));
@@ -219,7 +216,7 @@
 
     @Test public void named_graph_put_2() {
         try ( RDFConnection conn = connection() ) {
-            conn.put(graphName, model1); 
+            conn.put(graphName, model1);
             conn.put(graphName, model2);
             Model m = conn.fetch(graphName);
             assertTrue("Models not isomorphic", isomorphic(m, model2));
@@ -229,7 +226,7 @@
 
     @Test public void named_graph_put_2_different() {
         try ( RDFConnection conn = connection() ) {
-            conn.put(graphName, model1); 
+            conn.put(graphName, model1);
             conn.put(graphName2, model2);
             Model m1 = conn.fetch(graphName);
             Model m2 = conn.fetch(graphName2);
@@ -245,7 +242,7 @@
             assertTrue("Models not isomorphic", isomorphic(m, model1));
         }
     }
-    
+
     @Test public void named_graph_post_2() {
         try ( RDFConnection conn = connection() ) {
             conn.load(graphName, model1);
@@ -257,18 +254,39 @@
     }
 
     // DELETE
-    
-    // Remote connections don't support transactions fully.  
-    //@Test public void transaction_01() 
+
+    // Remote connections don't support transactions fully.
+    //@Test public void transaction_01()
 
     private static boolean isomorphic(Dataset ds1, Dataset ds2) {
         return IsoMatcher.isomorphic(ds1.asDatasetGraph(), ds2.asDatasetGraph());
     }
-    
+
     private static boolean isomorphic(Model model1, Model model2) {
         return model1.isIsomorphicWith(model2);
     }
-    
+
+    @Test public void query_01() {
+        try ( RDFConnection conn = connection() ) {
+            Txn.executeRead(conn, ()->{
+                try ( QueryExecution qExec = conn.query("SELECT ?x {}") ) {
+                    ResultSet rs = qExec.execSelect();
+                    ResultSetFormatter.consume(rs);
+                }
+            });
+        }
+    }
+
+    @Test public void query_02() {
+        try ( RDFConnection conn = connection() ) {
+            Txn.executeRead(conn, ()->{
+                try ( QueryExecution qExec = conn.query("ASK{}") ) {
+                    boolean b = qExec.execAsk();
+                    assertTrue(b);
+                }
+            });
+        }
+    }
 
     @Test public void query_ask_01() {
         try ( RDFConnection conn = connection() ) {
@@ -322,7 +340,7 @@
             assertEquals(2, m.size());
         }
     }
-    
+
     @Test public void update_01() {
         try ( RDFConnection conn = connection() ) {
             conn.update("INSERT DATA { <urn:x:s> <urn:x:p> <urn:x:o>}");
@@ -352,32 +370,32 @@
     }
     // Not all Transactional support abort.
     @Test public void transaction_commit_read_01() {
-        String testDataFile = DIR+"data.trig"; 
+        String testDataFile = DIR+"data.trig";
         try ( RDFConnection conn = connection() ) {
 
             conn.begin(ReadWrite.WRITE);
             conn.loadDataset(dataset);
             conn.commit();
             conn.end();
-            
+
             conn.begin(ReadWrite.READ);
             Model m = conn.fetch();
             assertTrue(isomorphic(m, dataset.getDefaultModel()));
             conn.end();
         }
     }
-    
+
     // Not all RDFConnections support abort.
     @Test public void transaction_abort_read02() {
         Assume.assumeTrue(supportsAbort());
-        
-        String testDataFile = DIR+"data.trig"; 
+
+        String testDataFile = DIR+"data.trig";
         try ( RDFConnection conn = connection() ) {
             conn.begin(ReadWrite.WRITE);
             conn.loadDataset(testDataFile);
             conn.abort();
             conn.end();
-            
+
             conn.begin(ReadWrite.READ);
             Model m = conn.fetch();
             assertTrue(m.isEmpty());