Skip to content

GH-3253: Apply transforms to present Elements of ExprFunctionOp instances. #3254

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 5 additions & 15 deletions jena-arq/src/main/java/org/apache/jena/sparql/expr/E_Exists.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,9 @@

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 ;

Expand All @@ -46,15 +43,8 @@ public E_Exists(Element el, Op op) {
}

@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
Expand All @@ -74,17 +64,17 @@ public boolean equals(Expr other, boolean bySyntax) {
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) ; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,49 +20,37 @@

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)
{
Expand All @@ -75,24 +63,24 @@ public int hashCode()
{
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) ; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,39 @@

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 */

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);
Expand All @@ -53,11 +68,46 @@ public Expr getArg(int i) {
@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) ;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could copySubstitute in principle delegate to applyNodeTransform with a NodeTransform based on the binding? Or are there subtle semantic difference?

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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
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;
Expand Down Expand Up @@ -48,11 +49,13 @@
*/
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
Expand Down Expand Up @@ -144,7 +147,10 @@ public ElementSubQuery transform(ElementSubQuery subQuery, Query newQuery) {
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);
});
Comment on lines +150 to +153
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This intent of this change is to remove the need for the explicit Map next to the NodeTransform.

if ( ! workToDo )
return data;

Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
Loading
Loading