This is an automated email from the ASF dual-hosted git repository.
andy pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/jena.git
The following commit(s) were added to refs/heads/main by this push:
new ee7c37c JENA-2135: Transform CONSTRUCT and DESCRIBE
new cd4e676 Merge pull request #1062 from afs/transform
ee7c37c is described below
commit ee7c37cde50ad4e559d98c4143bcf052304153ba
Author: Andy Seaborne <[email protected]>
AuthorDate: Thu Sep 2 17:37:49 2021 +0100
JENA-2135: Transform CONSTRUCT and DESCRIBE
---
.../org/apache/jena/sparql/syntax/Template.java | 50 +++----
.../syntax/syntaxtransform/QueryTransformOps.java | 96 +++++++++++---
.../syntaxtransform/TestSyntaxTransform.java | 147 ++++++++++++---------
3 files changed, 191 insertions(+), 102 deletions(-)
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/syntax/Template.java
b/jena-arq/src/main/java/org/apache/jena/sparql/syntax/Template.java
index 9131c0f..ade7d3c 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/syntax/Template.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/syntax/Template.java
@@ -37,24 +37,24 @@ import org.apache.jena.sparql.util.NodeIsomorphismMap;
/** Quads/Triples template. */
-public class Template
+public class Template
{
static final int HashTemplateGroup = 0xB1 ;
private final QuadAcc qp ;
private final BasicPattern bgp;
-
+
public Template(QuadAcc qp)
- {
+ {
this.qp = qp ;
this.bgp = null;
}
-
+
public Template(BasicPattern bgp)
- {
+ {
this.bgp = bgp;
this.qp = null;
}
-
+
// public void addTriple(Triple t) { quads.addTriple(t) ; }
// public int mark() { return quads.mark() ; }
// public void addTriple(int index, Triple t) { quads.addTriple(index, t) ;
}
@@ -64,7 +64,7 @@ public class Template
// public void addTriplePath(int index, TriplePath path)
// { throw new ARQException("Triples-only collector") ; }
-
+
public boolean containsRealQuad(){
for(Quad quad : this.getQuads()){
if ( ! Quad.defaultGraphNodeGenerated.equals( quad.getGraph())){
@@ -73,9 +73,9 @@ public class Template
}
return false;
}
-
+
public BasicPattern getBGP()
- {
+ {
if (this.bgp != null){
return this.bgp;
}
@@ -87,7 +87,7 @@ public class Template
return bgp;
}
public List<Triple> getTriples()
- {
+ {
if(this.bgp != null){
return this.bgp.getList();
}
@@ -102,13 +102,13 @@ public class Template
if( this.bgp != null){
List<Quad> quads = new ArrayList<>();
for(Triple triple: this.bgp.getList()){
- quads.add( new Quad( Quad.defaultGraphNodeGenerated,
triple ) );
+ quads.add( new Quad( Quad.defaultGraphNodeGenerated,
triple ) );
}
return quads;
- }
- return qp.getQuads() ;
+ }
+ return qp.getQuads() ;
}
-
+
public Map<Node, BasicPattern> getGraphPattern(){
List<Quad> quads = getQuads();
HashMap<Node, BasicPattern> graphs = new HashMap<>();
@@ -134,17 +134,17 @@ public class Template
}
}
- private int calcHashCode = -1 ;
+ private int calcHashCode = -1 ;
@Override
public int hashCode()
- {
- // BNode invariant hashCode.
+ {
+ // BNode invariant hashCode.
int calcHashCode = Template.HashTemplateGroup ;
for ( Quad q : getQuads() )
- calcHashCode ^= hash(q) ^ calcHashCode<<1 ;
+ calcHashCode ^= hash(q) ^ calcHashCode<<1 ;
return calcHashCode ;
}
-
+
private static int hash(Quad quad)
{
int hash = 0 ;
@@ -154,20 +154,20 @@ public class Template
hash = hashGraph(quad.getGraph()) ^ hash<<1 ;
return hash ;
}
-
+
private static int hashGraph(Node node){
- if ( node == null ) return Quad.defaultGraphNodeGenerated.hashCode() ;
+ if ( node == null ) return Quad.defaultGraphNodeGenerated.hashCode() ;
if ( node.isBlank() ) return 59 ;
return node.hashCode() ;
}
private static int hashNode(Node node)
{
- if ( node == null ) return 37 ;
+ if ( node == null ) return 37 ;
if ( node.isBlank() ) return 59 ;
return node.hashCode() ;
}
-
+
public boolean equalIso(Object temp2, NodeIsomorphismMap labelMap)
{
if ( ! ( temp2 instanceof Template) ) return false ;
@@ -175,7 +175,7 @@ public class Template
List<Quad> list1 = this.getQuads() ;
List<Quad> list2 = tg2.getQuads() ;
if ( list1.size() != list2.size() ) return false ;
-
+
for ( int i = 0 ; i < list1.size() ; i++ )
{
Quad q1 = list1.get(i) ;
@@ -187,7 +187,7 @@ public class Template
}
return true ;
}
-
+
public void format(FormatterTemplate fmtTemplate)
{
fmtTemplate.format(this) ;
diff --git
a/jena-arq/src/main/java/org/apache/jena/sparql/syntax/syntaxtransform/QueryTransformOps.java
b/jena-arq/src/main/java/org/apache/jena/sparql/syntax/syntaxtransform/QueryTransformOps.java
index b6b872b..5a8c286 100644
---
a/jena-arq/src/main/java/org/apache/jena/sparql/syntax/syntaxtransform/QueryTransformOps.java
+++
b/jena-arq/src/main/java/org/apache/jena/sparql/syntax/syntaxtransform/QueryTransformOps.java
@@ -18,6 +18,7 @@
package org.apache.jena.sparql.syntax.syntaxtransform;
+import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -29,22 +30,18 @@ import org.apache.jena.query.SortCondition;
import org.apache.jena.rdf.model.Literal ;
import org.apache.jena.rdf.model.RDFNode ;
import org.apache.jena.rdf.model.Resource ;
+import org.apache.jena.shared.JenaException;
import org.apache.jena.shared.PrefixMapping;
import org.apache.jena.shared.impl.PrefixMappingImpl;
import org.apache.jena.sparql.ARQException;
-import org.apache.jena.sparql.core.DatasetDescription;
-import org.apache.jena.sparql.core.Prologue;
-import org.apache.jena.sparql.core.Var;
-import org.apache.jena.sparql.core.VarExprList;
+import org.apache.jena.sparql.core.*;
import org.apache.jena.sparql.expr.Expr;
import org.apache.jena.sparql.expr.ExprTransform;
import org.apache.jena.sparql.expr.ExprTransformer;
import org.apache.jena.sparql.expr.ExprVar;
import org.apache.jena.sparql.graph.NodeTransform;
-import org.apache.jena.sparql.syntax.Element;
-import org.apache.jena.sparql.syntax.ElementData;
-import org.apache.jena.sparql.syntax.ElementGroup;
-import org.apache.jena.sparql.syntax.ElementSubQuery;
+import org.apache.jena.sparql.modify.request.QuadAcc;
+import org.apache.jena.sparql.syntax.*;
/** Support for transformation of query abstract syntax. */
public class QueryTransformOps {
@@ -71,15 +68,22 @@ public class QueryTransformOps {
*/
public static Query transform(Query query, ElementTransform transform,
ExprTransform exprTransform) {
Query q2 = QueryTransformOps.shallowCopy(query);
- // "Shallow copy with transform."
// Mutate the q2 structures which are already allocated and no other
code can access yet.
- mutateVarExprList(q2.getProject(), exprTransform);
+
+ mutateByQueryType(q2, transform, exprTransform);
mutateVarExprList(q2.getGroupBy(), exprTransform);
mutateExprList(q2.getHavingExprs(), exprTransform);
- if (q2.getOrderBy() != null) {
+ if (q2.getOrderBy() != null)
mutateSortConditions(q2.getOrderBy(), exprTransform);
+ mutateQueryPattern(q2, transform, exprTransform);
+ if ( query.isQueryResultStar() ) {
+ // Reset internal to only what now can be seen.
+ q2.resetResultVars();
}
+ return q2;
+ }
+ private static void mutateQueryPattern(Query q2, ElementTransform
transform, ExprTransform exprTransform) {
Element el = q2.getQueryPattern();
// Explicit null check to prevent warning in ElementTransformer
@@ -100,15 +104,43 @@ public class QueryTransformOps {
throw new ARQException("Can't transform a values data block to
a different type other than ElementData. "
+ "Transform yeld type " +
Objects.toString(rawElData2.getClass()));
}
-
ElementData elData2 = (ElementData)rawElData2;
q2.setValuesDataBlock(elData2.getVars(), elData2.getRows());
}
- if ( query.isQueryResultStar() ) {
- // Reset internal to only what now can be seen.
- q2.resetResultVars();
+ }
+
+ // Do the result form part of the cloned query.
+ private static void mutateByQueryType(Query q2, ElementTransform
transform, ExprTransform exprTransform) {
+ switch(q2.queryType()) {
+ case ASK : break;
+ case CONSTRUCT :
+ case CONSTRUCT_QUADS :
+ // Variables in CONSTRUCT template.
+ Template template = q2.getConstructTemplate();
+ QuadAcc acc = new QuadAcc();
+ List<Quad> quads = template.getQuads();
+ template.getQuads().forEach(q->{
+ Node g = transform(q.getGraph(), exprTransform);
+ Node s = transform(q.getSubject(), exprTransform);
+ Node p = transform(q.getPredicate(), exprTransform);
+ Node o = transform(q.getObject(), exprTransform);
+ acc.addQuad(Quad.create(g, s, p, o));
+ });
+ Template template2 = new Template(acc);
+ q2.setConstructTemplate(template2);
+ break;
+ case DESCRIBE :
+ // Variables in describe.
+ mutateDescribeVar(q2.getProjectVars(), q2.getResultURIs(),
exprTransform);
+ break;
+ case SELECT :
+ mutateVarExprList(q2.getProject(), exprTransform);
+ break;
+ case CONSTRUCT_JSON :
+ throw new UnsupportedOperationException("Transform of JSON
template queries");
+ case UNKNOWN :
+ throw new JenaException("Unknown qu ery type");
}
- return q2;
}
public static Query transform(Query query, ElementTransform transform) {
@@ -143,6 +175,23 @@ public class QueryTransformOps {
varExprList.addAll(x);
}
+ private static void mutateDescribeVar(List<Var> varList, List<Node>
constants, ExprTransform exprTransform) {
+ List<Var> varList2 = new ArrayList<>(varList.size());
+ for (Var v : varList) {
+ Node n = transform(v, exprTransform);
+ if ( n != v ) {
+ if ( !constants.contains(n) )
+ constants.add(n);
+ continue;
+ }
+ varList2.add(v);
+ }
+ if ( varList2.size() != varList.size() ) {
+ varList.clear();
+ varList.addAll(varList2);
+ }
+ }
+
private static VarExprList transformVarExprList(VarExprList varExprList,
ExprTransform exprTransform) {
VarExprList varExprList2 = new VarExprList();
boolean changed = false;
@@ -184,6 +233,21 @@ public class QueryTransformOps {
return varExprList2;
}
+ // Transform a variable node (for low-usage cases).
+ // Returns node object for "no transform"
+ private static Node transform(Node node, ExprTransform exprTransform) {
+ if ( ! Var.isVar(node) )
+ return node;
+ Var v = Var.alloc(node);
+ ExprVar ev = new ExprVar(v);
+ Expr e2 = exprTransform.transform(ev);
+ if (e2 == null || e2 == ev )
+ return node;
+ if ( ! e2.isConstant() )
+ return node ;
+ return e2.getConstant().getNode();
+ }
+
static class QueryShallowCopy implements QueryVisitor {
final Query newQuery = new Query();
diff --git
a/jena-arq/src/test/java/org/apache/jena/sparql/syntax/syntaxtransform/TestSyntaxTransform.java
b/jena-arq/src/test/java/org/apache/jena/sparql/syntax/syntaxtransform/TestSyntaxTransform.java
index cc533fc..dc25521 100644
---
a/jena-arq/src/test/java/org/apache/jena/sparql/syntax/syntaxtransform/TestSyntaxTransform.java
+++
b/jena-arq/src/test/java/org/apache/jena/sparql/syntax/syntaxtransform/TestSyntaxTransform.java
@@ -20,19 +20,19 @@ package org.apache.jena.sparql.syntax.syntaxtransform;
import static org.junit.Assert.assertEquals;
-import java.util.HashMap ;
-import java.util.Map ;
-
-import org.apache.jena.graph.Node ;
-import org.apache.jena.query.Query ;
-import org.apache.jena.query.QueryFactory ;
-import org.apache.jena.rdf.model.RDFNode ;
-import org.apache.jena.sparql.core.Var ;
-import org.apache.jena.sparql.sse.SSE ;
-import org.apache.jena.sparql.util.ModelUtils ;
-import org.apache.jena.update.UpdateFactory ;
-import org.apache.jena.update.UpdateRequest ;
-import org.junit.Test ;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.jena.graph.Node;
+import org.apache.jena.query.Query;
+import org.apache.jena.query.QueryFactory;
+import org.apache.jena.rdf.model.RDFNode;
+import org.apache.jena.sparql.core.Var;
+import org.apache.jena.sparql.sse.SSE;
+import org.apache.jena.sparql.util.ModelUtils;
+import org.apache.jena.update.UpdateFactory;
+import org.apache.jena.update.UpdateRequest;
+import org.junit.Test;
/** Test of variable replaced by value */
public class TestSyntaxTransform
@@ -40,24 +40,22 @@ public class TestSyntaxTransform
@Test public void subst_query_01() {
testQuery("SELECT * { }",
"SELECT * {}",
- "o", "1");
- }
+ "o", "1"); }
@Test public void subst_query_02() {
testQuery("SELECT ?x { }",
"SELECT ?x {}",
- "o", "1") ;
- }
+ "o", "1"); }
@Test public void subst_query_03() {
testQuery("SELECT ?o { }",
"SELECT (1 as ?o) {}",
- "o", "1") ; }
+ "o", "1"); }
@Test public void subst_query_04() {
testQuery("SELECT (?o AS ?z) { }",
"SELECT (1 AS ?z) {}",
- "o", "1") ; }
+ "o", "1"); }
@Test public void subst_query_05() {
testQuery("SELECT (?o+2 AS ?z) { }",
@@ -80,71 +78,98 @@ public class TestSyntaxTransform
@Test public void subst_query_11() {
testQuery("SELECT * { ?s ?p ?o { SELECT ?x { ?x ?p ?o } } }",
"SELECT * { ?s ?p 1 { SELECT ?x { ?x ?p 1 } } }",
- "o", "1") ; }
+ "o", "1"); }
@Test public void subst_query_20() {
testQuery("SELECT * { ?s ?p ?g GRAPH ?g { ?s ?p ?g } }",
"SELECT * { ?s ?p <urn:ex:graph> GRAPH <urn:ex:graph> { ?s
?p <urn:ex:graph> } }",
- "g", "<urn:ex:graph>") ; }
+ "g", "<urn:ex:graph>"); }
@Test public void subst_query_21() {
testQuery("SELECT * { ?s ?p ?srv SERVICE ?srv { ?s ?p ?srv}}",
"SELECT * { ?s ?p <urn:ex:service> SERVICE <urn:ex:service>
{ ?s ?p <urn:ex:service>}}",
- "srv", "<urn:ex:service>") ; }
+ "srv", "<urn:ex:service>"); }
@Test public void subst_query_30() {
- testQuery("SELECT * { ?s ?p ?o } ORDER BY ?s", "SELECT * { <urn:ex:z>
?p ?o } ORDER BY (<urn:ex:z>)",
- "s", "<urn:ex:z>");
+ testQuery("CONSTRUCT { ?s ?p ?o } WHERE { ?s ?p ?o }",
+ "CONSTRUCT { ?s ?p 1 } WHERE { ?s ?p 1 } ",
+ "o", "1"); }
+
+ @Test public void subst_query_31() {
+ testQuery("CONSTRUCT { GRAPH ?g { ?s ?p ?g } } WHERE { ?s ?p ?g }",
+ "CONSTRUCT { GRAPH <urn:x:g> { ?s ?p <urn:x:g>} } WHERE { ?s
?p <urn:x:g> }",
+ "g", "<urn:x:g>"); }
+
+ @Test public void subst_query_40() {
+ testQuery("DESCRIBE ?o ?x",
+ "DESCRIBE ?x <urn:x:obj>",
+ "o", "<urn:x:obj>"); }
+
+ @Test public void subst_query_41() {
+ testQuery("DESCRIBE ?o ?x WHERE { ?s ?p ?o } ",
+ "DESCRIBE ?x <urn:x:obj> WHERE { ?s ?p <urn:x:obj> } ",
+ "o", "<urn:x:obj>"); }
+
+ @Test public void subst_query_50() {
+ testQuery("ASK { ?s ?p ?o } ",
+ "ASK { ?s ?p <urn:x:obj> } ",
+ "o", "<urn:x:obj>"); }
+
+ @Test public void subst_query_model_1() {
+ testQuery("SELECT * { ?s ?p ?o } ORDER BY ?s",
+ "SELECT * { <urn:ex:z> ?p ?o } ORDER BY (<urn:ex:z>)",
+ "s", "<urn:ex:z>");
}
// Same except use the Model API.
- @Test public void subst_query_31() {
- testQueryModel("SELECT * { ?s ?p ?o } ORDER BY ?s", "SELECT * {
<urn:ex:z> ?p ?o } ORDER BY (<urn:ex:z>)",
+ @Test public void subst_query_model_2() {
+ testQueryModel("SELECT * { ?s ?p ?o } ORDER BY ?s",
+ "SELECT * { <urn:ex:z> ?p ?o } ORDER BY (<urn:ex:z>)",
"s", "<urn:ex:z>");
}
@Test public void subst_update_01() {
testUpdate("DELETE { ?s <urn:ex:p> ?x } WHERE {}",
"DELETE { ?s <urn:ex:p> <urn:ex:z> } WHERE {}",
- "x", "<urn:ex:z>") ;
+ "x", "<urn:ex:z>");
}
@Test public void subst_update_02() {
testUpdate("DELETE { ?s <urn:ex:p> ?x } WHERE { ?s <urn:ex:p> ?x }",
"DELETE { ?s <urn:ex:p> <urn:ex:z> } WHERE { ?s <urn:ex:p>
<urn:ex:z> }",
- "x", "<urn:ex:z>") ;
+ "x", "<urn:ex:z>");
}
@Test public void subst_update_03() {
testUpdate("DELETE { ?s <urn:ex:p> ?x } INSERT { ?s <urn:ex:p> ?x }
WHERE { ?s <urn:ex:p> ?x }",
"DELETE { ?s <urn:ex:p> <urn:ex:z> } INSERT { ?s <urn:ex:p>
<urn:ex:z> } WHERE { ?s <urn:ex:p> <urn:ex:z> }",
- "x", "<urn:ex:z>") ;
+ "x", "<urn:ex:z>");
}
@Test public void subst_update_09() {
testUpdate("DELETE WHERE { ?s <urn:ex:p> ?x }",
"DELETE WHERE { ?s <urn:ex:p> <urn:ex:z> }",
- "x", "<urn:ex:z>") ;
+ "x", "<urn:ex:z>");
}
@Test public void subst_update_10() {
testUpdateModel("DELETE WHERE { ?s <urn:ex:p> ?x }",
"DELETE WHERE { ?s <urn:ex:p> <urn:ex:z> }",
- "x", "<urn:ex:z>") ;
+ "x", "<urn:ex:z>");
}
- //static final String PREFIX = "PREFIX : <http://example/>\n" ;
- static final String PREFIX = "" ;
+ //static final String PREFIX = "PREFIX : <http://example/>\n";
+ static final String PREFIX = "";
private void testQuery(String input, String output, String varStr, String
valStr)
{
- Query q1 = QueryFactory.create(PREFIX+input) ;
- Query qExpected = QueryFactory.create(PREFIX+output) ;
+ Query q1 = QueryFactory.create(PREFIX+input);
+ Query qExpected = QueryFactory.create(PREFIX+output);
- Map<Var, Node> map = new HashMap<>() ;
- map.put(Var.alloc(varStr), SSE.parseNode(valStr)) ;
+ Map<Var, Node> map = new HashMap<>();
+ map.put(Var.alloc(varStr), SSE.parseNode(valStr));
- Query qTrans = QueryTransformOps.transform(q1, map) ;
+ Query qTrans = QueryTransformOps.transform(q1, map);
if ( ! qExpected.equals(qTrans) ) {
System.out.println(qExpected.getProject());
@@ -153,54 +178,54 @@ public class TestSyntaxTransform
System.out.print(qTrans);
}
- assertEquals(qExpected, qTrans) ;
+ assertEquals(qExpected, qTrans);
}
private void testQueryModel(String input, String output, String varStr,
String valStr) {
- Query q1 = QueryFactory.create(PREFIX+input) ;
- Query qExpected = QueryFactory.create(PREFIX+output) ;
+ Query q1 = QueryFactory.create(PREFIX+input);
+ Query qExpected = QueryFactory.create(PREFIX+output);
- Map<String, RDFNode> map = new HashMap<>() ;
+ Map<String, RDFNode> map = new HashMap<>();
Node n = SSE.parseNode(valStr);
RDFNode x = ModelUtils.convertGraphNodeToRDFNode(n);
map.put(varStr, x);
- Query qTrans = QueryTransformOps.transformQuery(q1, map) ;
- assertEquals(qExpected, qTrans) ;
+ Query qTrans = QueryTransformOps.transformQuery(q1, map);
+ assertEquals(qExpected, qTrans);
}
private void testUpdate(String input, String output, String varStr, String
valStr) {
- UpdateRequest req1 = UpdateFactory.create(PREFIX+input) ;
- UpdateRequest reqExpected = UpdateFactory.create(PREFIX+output) ;
+ UpdateRequest req1 = UpdateFactory.create(PREFIX+input);
+ UpdateRequest reqExpected = UpdateFactory.create(PREFIX+output);
- Map<Var, Node> map = new HashMap<>() ;
- map.put(Var.alloc(varStr), SSE.parseNode(valStr)) ;
+ Map<Var, Node> map = new HashMap<>();
+ map.put(Var.alloc(varStr), SSE.parseNode(valStr));
- UpdateRequest reqTrans = UpdateTransformOps.transform(req1, map) ;
+ UpdateRequest reqTrans = UpdateTransformOps.transform(req1, map);
// Crude.
- String x1 = reqExpected.toString().replaceAll("[ \n\t]", "") ;
- String x2 = reqTrans.toString().replaceAll("[ \n\t]", "") ;
- //assertEquals(reqExpected, reqTrans) ;
- assertEquals(x1, x2) ;
+ String x1 = reqExpected.toString().replaceAll("[ \n\t]", "");
+ String x2 = reqTrans.toString().replaceAll("[ \n\t]", "");
+ //assertEquals(reqExpected, reqTrans);
+ assertEquals(x1, x2);
}
private void testUpdateModel(String input, String output, String varStr,
String valStr) {
- UpdateRequest req1 = UpdateFactory.create(PREFIX+input) ;
- UpdateRequest reqExpected = UpdateFactory.create(PREFIX+output) ;
+ UpdateRequest req1 = UpdateFactory.create(PREFIX+input);
+ UpdateRequest reqExpected = UpdateFactory.create(PREFIX+output);
- Map<String, RDFNode> map = new HashMap<>() ;
+ Map<String, RDFNode> map = new HashMap<>();
Node n = SSE.parseNode(valStr);
RDFNode x = ModelUtils.convertGraphNodeToRDFNode(n);
map.put(varStr, x);
- UpdateRequest reqTrans = UpdateTransformOps.transformUpdate(req1, map)
;
+ UpdateRequest reqTrans = UpdateTransformOps.transformUpdate(req1, map);
// Crude.
- String x1 = reqExpected.toString().replaceAll("[ \n\t]", "") ;
- String x2 = reqTrans.toString().replaceAll("[ \n\t]", "") ;
- //assertEquals(reqExpected, reqTrans) ;
- assertEquals(x1, x2) ;
+ String x1 = reqExpected.toString().replaceAll("[ \n\t]", "");
+ String x2 = reqTrans.toString().replaceAll("[ \n\t]", "");
+ //assertEquals(reqExpected, reqTrans);
+ assertEquals(x1, x2);
}
}