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 c315fc3931 GH-3253: Apply transforms to present Elements of
ExprFunctionOp instances.
c315fc3931 is described below
commit c315fc3931ff68153fb68ff2f97c18db7ba114a0
Author: Claus Stadler <[email protected]>
AuthorDate: Sun Jun 15 18:10:29 2025 +0200
GH-3253: Apply transforms to present Elements of ExprFunctionOp instances.
---
.../java/org/apache/jena/sparql/expr/E_Exists.java | 20 ++----
.../org/apache/jena/sparql/expr/E_NotExists.java | 30 +++-----
.../apache/jena/sparql/expr/ExprFunctionOp.java | 56 ++++++++++++++-
.../syntaxtransform/ElementTransformSubst.java | 14 ++--
.../java/org/apache/jena/sparql/expr/TS_Expr.java | 2 +
.../expr/TestExprFunctionOp_ExprTransform.java | 67 ++++++++++++++++++
.../expr/TestExprFunctionOp_NodeTransform.java | 81 ++++++++++++++++++++++
.../apache/jena/sparql/expr/TestExprTransform.java | 18 ++---
8 files changed, 236 insertions(+), 52 deletions(-)
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/expr/E_Exists.java
b/jena-arq/src/main/java/org/apache/jena/sparql/expr/E_Exists.java
index 75e13f9d88..3592b37c69 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/expr/E_Exists.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/expr/E_Exists.java
@@ -20,12 +20,9 @@ package org.apache.jena.sparql.expr;
import org.apache.jena.sparql.algebra.Algebra ;
import org.apache.jena.sparql.algebra.Op ;
-import org.apache.jena.sparql.core.Substitute ;
import org.apache.jena.sparql.engine.QueryIterator ;
import org.apache.jena.sparql.engine.binding.Binding ;
import org.apache.jena.sparql.function.FunctionEnv ;
-import org.apache.jena.sparql.graph.NodeTransform;
-import org.apache.jena.sparql.graph.NodeTransformLib ;
import org.apache.jena.sparql.sse.Tags ;
import org.apache.jena.sparql.syntax.Element ;
@@ -46,15 +43,8 @@ public class E_Exists extends ExprFunctionOp
}
@Override
- public Expr copySubstitute(Binding binding) {
- Op op2 = Substitute.substitute(getGraphPattern(), binding) ;
- return new E_Exists(getElement(), op2) ;
- }
-
- @Override
- public Expr applyNodeTransform(NodeTransform nodeTransform) {
- Op op2 = NodeTransformLib.transform(nodeTransform, getGraphPattern()) ;
- return new E_Exists(getElement(), op2) ;
+ protected Expr copy(Element elt, Op op) {
+ return new E_Exists(elt, op) ;
}
@Override
@@ -74,17 +64,17 @@ public class E_Exists extends ExprFunctionOp
if ( this == other ) return true ;
if ( ! ( other instanceof E_Exists ) )
return false ;
-
+
E_Exists ex = (E_Exists)other ;
if ( bySyntax )
return this.getElement().equals(ex.getElement()) ;
else
return this.getGraphPattern().equals(ex.getGraphPattern()) ;
}
-
+
@Override
public ExprFunctionOp copy(ExprList args, Op x) { return new E_Exists(x) ;
}
-
+
@Override
public ExprFunctionOp copy(ExprList args, Element elPattern) { return new
E_Exists(elPattern) ; }
diff --git
a/jena-arq/src/main/java/org/apache/jena/sparql/expr/E_NotExists.java
b/jena-arq/src/main/java/org/apache/jena/sparql/expr/E_NotExists.java
index 158a839216..23f65048a9 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/expr/E_NotExists.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/expr/E_NotExists.java
@@ -20,49 +20,37 @@ package org.apache.jena.sparql.expr;
import org.apache.jena.sparql.algebra.Algebra ;
import org.apache.jena.sparql.algebra.Op ;
-import org.apache.jena.sparql.core.Substitute ;
import org.apache.jena.sparql.engine.QueryIterator ;
import org.apache.jena.sparql.engine.binding.Binding ;
import org.apache.jena.sparql.function.FunctionEnv ;
-import org.apache.jena.sparql.graph.NodeTransform;
-import org.apache.jena.sparql.graph.NodeTransformLib ;
import org.apache.jena.sparql.sse.Tags ;
import org.apache.jena.sparql.syntax.Element ;
public class E_NotExists extends ExprFunctionOp
{
- // Translated to "(not (exists (...)))"
+ // Translated to "(not (exists (...)))"
private static final String symbol = Tags.tagNotExists ;
public E_NotExists(Op op)
{
this(null, op) ;
}
-
+
public E_NotExists(Element elt)
{
this(elt, Algebra.compile(elt)) ;
}
-
+
public E_NotExists(Element el, Op op)
{
super(symbol, el, op) ;
}
@Override
- public Expr copySubstitute(Binding binding)
- {
- Op op2 = Substitute.substitute(getGraphPattern(), binding) ;
- return new E_NotExists(getElement(), op2) ;
+ protected Expr copy(Element elt, Op op) {
+ return new E_NotExists(elt, op) ;
}
- @Override
- public Expr applyNodeTransform(NodeTransform nodeTransform)
- {
- Op op2 = NodeTransformLib.transform(nodeTransform, getGraphPattern()) ;
- return new E_NotExists(getElement(), op2) ;
- }
-
@Override
protected NodeValue eval(Binding binding, QueryIterator qIter, FunctionEnv
env)
{
@@ -75,24 +63,24 @@ public class E_NotExists extends ExprFunctionOp
{
return symbol.hashCode() ^ getGraphPattern().hashCode() ;
}
-
+
@Override
public boolean equals(Expr other, boolean bySyntax) {
if ( other == null ) return false ;
if ( this == other ) return true ;
if ( ! ( other instanceof E_NotExists ) )
return false ;
-
+
E_NotExists ex = (E_NotExists)other ;
if ( bySyntax )
return this.getElement().equals(ex.getElement()) ;
else
return this.getGraphPattern().equals(ex.getGraphPattern()) ;
}
-
+
@Override
public ExprFunctionOp copy(ExprList args, Op x) { return new
E_NotExists(x) ; }
-
+
@Override
public ExprFunctionOp copy(ExprList args, Element elPattern) { return new
E_NotExists(elPattern) ; }
}
diff --git
a/jena-arq/src/main/java/org/apache/jena/sparql/expr/ExprFunctionOp.java
b/jena-arq/src/main/java/org/apache/jena/sparql/expr/ExprFunctionOp.java
index f8edeed603..e0ca844837 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/expr/ExprFunctionOp.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/expr/ExprFunctionOp.java
@@ -18,16 +18,29 @@
package org.apache.jena.sparql.expr;
+import java.util.Map;
+
+import org.apache.jena.graph.Node;
import org.apache.jena.sparql.algebra.Op;
+import org.apache.jena.sparql.algebra.OpAsQuery;
+import org.apache.jena.sparql.core.Substitute;
+import org.apache.jena.sparql.core.Var;
import org.apache.jena.sparql.engine.ExecutionContext;
import org.apache.jena.sparql.engine.QueryIterator;
import org.apache.jena.sparql.engine.binding.Binding;
+import org.apache.jena.sparql.engine.binding.BindingLib;
import org.apache.jena.sparql.engine.iterator.QueryIterSingleton;
import org.apache.jena.sparql.engine.iterator.QueryIteratorCheck;
import org.apache.jena.sparql.engine.main.QC;
import org.apache.jena.sparql.function.FunctionEnv;
+import org.apache.jena.sparql.graph.NodeTransform;
+import org.apache.jena.sparql.graph.NodeTransformLib;
import org.apache.jena.sparql.syntax.Element;
-
+import org.apache.jena.sparql.syntax.syntaxtransform.ElementTransform;
+import org.apache.jena.sparql.syntax.syntaxtransform.ElementTransformSubst;
+import org.apache.jena.sparql.syntax.syntaxtransform.ElementTransformer;
+import org.apache.jena.sparql.syntax.syntaxtransform.ExprTransformNodeElement;
+import org.apache.jena.sparql.syntax.syntaxtransform.NodeTransformSubst;
/** A "function" that executes over a pattern */
@@ -35,7 +48,9 @@ public abstract class ExprFunctionOp extends ExprFunction
{
private final Op op;
private Op opRun = null;
- private final Element element;
+
+ // If element has not been set via the ctor then getElement() will compute
it lazily.
+ private Element element;
protected ExprFunctionOp(String fName, Element el, Op op) {
super(fName);
@@ -53,11 +68,46 @@ public abstract class ExprFunctionOp extends ExprFunction
@Override
public Op getGraphPattern() { return op; }
- public Element getElement() { return element; }
+ public Element getElement() {
+ if (element == null) {
+ element = OpAsQuery.asElement(op);
+ }
+ return element;
+ }
@Override
public int numArgs() { return 0; }
+ @Override
+ public Expr copySubstitute(Binding binding) {
+ Op op2 = Substitute.substitute(getGraphPattern(), binding) ;
+ Element elt = getElement();
+ Element elt2 = null;
+ if (elt != null) {
+ Map<Var, Node> map = BindingLib.bindingToMap(binding);
+ NodeTransform nodeTransform = new NodeTransformSubst(map);
+ ElementTransform eltTransform = new
ElementTransformSubst(nodeTransform);
+ ExprTransform exprTransform = new
ExprTransformNodeElement(nodeTransform, eltTransform);
+ elt2 = ElementTransformer.transform(elt, eltTransform,
exprTransform);
+ }
+ return copy(elt2, op2) ;
+ }
+
+ @Override
+ public Expr applyNodeTransform(NodeTransform nodeTransform) {
+ Op op2 = NodeTransformLib.transform(nodeTransform, getGraphPattern()) ;
+ Element elt = getElement();
+ Element elt2 = null;
+ if (elt != null) {
+ ElementTransform eltTransform = new
ElementTransformSubst(nodeTransform);
+ ExprTransform exprTransform = new
ExprTransformNodeElement(nodeTransform, eltTransform);
+ elt2 = ElementTransformer.transform(elt, eltTransform,
exprTransform);
+ }
+ return copy(elt2, op2) ;
+ }
+
+ protected abstract Expr copy(Element elt, Op op);
+
// ---- Evaluation
@Override
diff --git
a/jena-arq/src/main/java/org/apache/jena/sparql/syntax/syntaxtransform/ElementTransformSubst.java
b/jena-arq/src/main/java/org/apache/jena/sparql/syntax/syntaxtransform/ElementTransformSubst.java
index f94a0a301e..3b57217029 100644
---
a/jena-arq/src/main/java/org/apache/jena/sparql/syntax/syntaxtransform/ElementTransformSubst.java
+++
b/jena-arq/src/main/java/org/apache/jena/sparql/syntax/syntaxtransform/ElementTransformSubst.java
@@ -21,6 +21,7 @@ package org.apache.jena.sparql.syntax.syntaxtransform;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import org.apache.jena.graph.Node;
import org.apache.jena.graph.Node_Variable;
@@ -48,11 +49,13 @@ import org.apache.jena.sparql.syntax.*;
*/
public class ElementTransformSubst extends ElementTransformCopyBase {
private final NodeTransform nodeTransform;
- private final Map<Var, ? extends Node> mapping;
public ElementTransformSubst(Map<Var, ? extends Node> mapping) {
- this.mapping = mapping;
- this.nodeTransform = new NodeTransformSubst(mapping);
+ this(new NodeTransformSubst(mapping));
+ }
+
+ public ElementTransformSubst(NodeTransform nodeTransform) {
+ this.nodeTransform = Objects.requireNonNull(nodeTransform);
}
@Override
@@ -144,7 +147,10 @@ public class ElementTransformSubst extends
ElementTransformCopyBase {
public ElementData transform(ElementData data) {
// Check for var-var. If none, no work to do.
List<Var> vars = data.getVars();
- boolean workToDo = vars.stream().anyMatch(v->mapping.containsKey(v));
+ boolean workToDo = vars.stream().anyMatch(v-> {
+ Node n = nodeTransform.apply(v);
+ return n != null && !v.equals(n);
+ });
if ( ! workToDo )
return data;
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/expr/TS_Expr.java
b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TS_Expr.java
index 10912d5787..89ee2ac256 100644
--- a/jena-arq/src/test/java/org/apache/jena/sparql/expr/TS_Expr.java
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TS_Expr.java
@@ -51,6 +51,8 @@ import org.junit.runners.Suite.SuiteClasses;
, TestCustomAggregates.class
, TestStatisticsAggregates.class
, TestNodeValueSortKey.class
+ , TestExprFunctionOp_NodeTransform.class
+ , TestExprFunctionOp_ExprTransform.class
})
public class TS_Expr
diff --git
a/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestExprFunctionOp_ExprTransform.java
b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestExprFunctionOp_ExprTransform.java
new file mode 100644
index 0000000000..a881888a4f
--- /dev/null
+++
b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestExprFunctionOp_ExprTransform.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.sparql.expr;
+
+import static org.junit.Assert.assertEquals;
+
+import org.apache.jena.sparql.algebra.walker.Walker;
+import
org.apache.jena.sparql.expr.TestExprFunctionOp_NodeTransform.ExprFunctionOpValidator;
+import org.apache.jena.sparql.util.ExprUtils;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestExprFunctionOp_ExprTransform {
+ private ExprTransform et = new ExprTransformCopy()
+ { @Override
+ public Expr transform(ExprVar exprVar)
+ { return new ExprVar(exprVar.getVarName().toUpperCase()); }
+ };
+
+ @Test public void et_exists_tp_01() { test( "exists { ?s ?p ?o
}", "exists { ?s ?p ?o }", et); }
+ @Test public void et_notExists_tp_01() { test("not exists { ?s ?p ?o
}", "notexists { ?s ?p ?o }", et); }
+
+ // Note: The empty graph pattern in "{} FILTER(...)" is used to establish
+ // syntactic equivalence with the transformation result.
+ @Test public void et_exists_filter_01() { test( "exists { {}
FILTER(?x = ?y) }", "exists { {} FILTER(?X = ?Y) }", et); }
+ @Test public void et_notExists_filter_01() { test("not exists { {}
FILTER(?x = ?y) }", "not exists { {} FILTER(?X = ?Y) }", et); }
+
+ @Test public void et_exists_bind_01() { test( "exists { BIND(?x AS
?y) }", "exists { BIND(?X AS ?y) }", et); }
+ @Test public void et_notExists_bind_01() { test("not exists { BIND(?x AS
?y) }", "not exists { BIND(?X AS ?y) }", et); }
+
+ @Test public void et_exists_nested_filter_01() { test( "exists { {}
FILTER exists { {} FILTER(?x = ?y) } }", "exists { {} FILTER exists
{ {} FILTER(?X = ?Y) } }", et); }
+ @Test public void et_notExists_nested_filter_01() { test("not exists { {}
FILTER not exists { {} FILTER(?x = ?y) } }", "not exists { {} FILTER not exists
{ {} FILTER(?X = ?Y) } }", et); }
+
+ private void test(String string, String string2, ExprTransform et)
+ {
+ Expr e1 = ExprUtils.parse(string);
+ Expr e2 = ExprUtils.parse(string2);
+
+ Expr e3 = ExprTransformer.transform(et, e1);
+
+ // Check whether syntax and algebra are consistent
+ ExprVisitor opVisitor = new ExprFunctionOpValidator();
+ Walker.walk(e2, opVisitor);
+ Walker.walk(e3, opVisitor);
+
+ assertEquals(e2, e3) ;
+ if (!e2.equalsBySyntax(e3)) {
+ Assert.fail("Objects differ by syntax: " + e2 + " != " + e3);
+ }
+ }
+}
diff --git
a/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestExprFunctionOp_NodeTransform.java
b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestExprFunctionOp_NodeTransform.java
new file mode 100644
index 0000000000..31d8de126a
--- /dev/null
+++
b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestExprFunctionOp_NodeTransform.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.sparql.expr;
+
+import static org.junit.Assert.assertEquals;
+
+import org.apache.jena.sparql.algebra.Algebra;
+import org.apache.jena.sparql.algebra.Op;
+import org.apache.jena.sparql.algebra.walker.Walker;
+import org.apache.jena.sparql.core.Var;
+import org.apache.jena.sparql.graph.NodeTransform;
+import org.apache.jena.sparql.graph.NodeTransformLib;
+import org.apache.jena.sparql.syntax.Element;
+import org.apache.jena.sparql.util.ExprUtils;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestExprFunctionOp_NodeTransform {
+ private NodeTransform nt = node -> node instanceof Var v
+ ? Var.alloc(v.getVarName().toUpperCase())
+ : node;
+
+ @Test public void nt_exists_tp_01() { test( "exists { ?s ?p ?o
}", "exists { ?S ?P ?O }", nt); }
+ @Test public void nt_notExists_tp_01() { test("not exists { ?s ?p ?o
}", "notexists { ?S ?P ?O }", nt); }
+
+ @Test public void nt_exists_filter_01() { test( "exists { FILTER(?x
= ?y) }", "exists { FILTER(?X = ?Y) }", nt); }
+ @Test public void nt_notExists_filter_01() { test("not exists { FILTER(?x
= ?y) }", "not exists { FILTER(?X = ?Y) }", nt); }
+
+ @Test public void nt_exists_bind_01() { test( "exists { BIND(?x AS
?y) }", "exists { BIND(?X AS ?Y) }", nt ); }
+ @Test public void nt_notExists_bind_01() { test("not exists { BIND(?x AS
?y) }", "not exists { BIND(?X AS ?Y) }", nt ); }
+
+ @Test public void nt_exists_nested_filter_01() { test( "exists { {}
FILTER exists { {} FILTER(?x = ?y) } }", "exists { {} FILTER exists
{ {} FILTER(?X = ?Y) } }", nt); }
+ @Test public void nt_notExists_nested_filter_01() { test("not exists { {}
FILTER not exists { {} FILTER(?x = ?y) } }", "not exists { {} FILTER not exists
{ {} FILTER(?X = ?Y) } }", nt); }
+
+ private void test(String string, String string2, NodeTransform nt)
+ {
+ Expr e1 = ExprUtils.parse(string);
+ Expr e2 = ExprUtils.parse(string2);
+
+ Expr e3 = NodeTransformLib.transform(nt, e1);
+
+ // Check whether syntax and algebra are consistent
+ ExprVisitor opVisitor = new ExprFunctionOpValidator();
+ Walker.walk(e2, opVisitor);
+ Walker.walk(e3, opVisitor);
+
+ assertEquals(e2, e3);
+ if (!e2.equalsBySyntax(e3)) {
+ Assert.fail("Objects differ by syntax: " + e2 + " != " + e3);
+ }
+ }
+
+ /** This class asserts that Op and Element of an ExprFunctionOp are
consistent. */
+ static class ExprFunctionOpValidator
+ extends ExprVisitorBase
+ {
+ @Override
+ public void visit(ExprFunctionOp op) {
+ Element elt = op.getElement();
+ Op actual = op.getGraphPattern();
+ Op expected = Algebra.compile(elt);
+ Assert.assertEquals(expected, actual);
+ }
+ }
+}
diff --git
a/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestExprTransform.java
b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestExprTransform.java
index 653d3f61ed..092def7cb3 100644
--- a/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestExprTransform.java
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestExprTransform.java
@@ -25,29 +25,29 @@ import org.junit.Test ;
public class TestExprTransform
{
- ExprTransform et1 = new ExprTransformCopy()
+ ExprTransform et1 = new ExprTransformCopy()
{ @Override
- public Expr transform(ExprVar exprVar)
- { return new ExprVar(exprVar.getVarName().toUpperCase()) ; }
+ public Expr transform(ExprVar exprVar)
+ { return new ExprVar(exprVar.getVarName().toUpperCase()) ; }
} ;
-
+
@Test public void exprTransform_01() { test("?v", "?V", et1 ) ; }
@Test public void exprTransform_02() { test("(+ ?v 1)", "(+ ?V 1)", et1
) ; }
@Test public void exprTransform_03() { test("(str (+ ?v 1))", "(str (+
?V 1))", et1 ) ; }
@Test public void exprTransform_04() { test("(if (+ ?v 1) ?a ?b)", "(if
(+ ?V 1) ?A ?B)", et1 ) ; }
-
+
// 2 or 3 ?
- @Test public void exprTransform_05() { test("(regex ?a ?b ?c)", "(regex
?A ?B ?C)", et1) ; }
+ @Test public void exprTransform_05() { test("(regex ?a ?b ?c)", "(regex
?A ?B ?C)", et1) ; }
@Test public void exprTransform_06() { test("(regex ?a ?b)", "(regex ?A
?B)", et1) ; }
-
+
private void test(String string, String string2, ExprTransform et)
{
Expr e1 = SSE.parseExpr(string) ;
Expr e2 = SSE.parseExpr(string2) ;
-
+
Expr e3 = ExprTransformer.transform(et, e1) ;
assertEquals(e2, e3) ;
-
+
}
}