Repository: apache/jena (java)

Issue:
ExprFunctionOp: Inconsistent Element and Op after transforms
### Version

jena-5.5.0-SNAPSHOT

### What happened?

Element and Op of `E_Exists` and `E_NotExists` become inconsistent after transform because Element is never updated. A syntax transform that renames variables in the Element thus has no effect.

The original code of these classes looks as follows:

```java
public Expr copySubstitute(Binding binding)
{
    Op op2 = Substitute.substitute(getGraphPattern(), binding) ;
    return new E_NotExists(getElement(), op2) ; // !!! Element unchanged.
}
 
@Override
public Expr applyNodeTransform(NodeTransform nodeTransform)
{
    Op op2 = NodeTransformLib.transform(nodeTransform, getGraphPattern()) ;
    return new E_NotExists(getElement(), op2) ; /// Element unchanged.
}
```


### Relevant output and stacktrace

```shell

```

### Are you interested in making a pull request?

Yes

Pull request:
GH-3253: Apply transforms to present Elements of ExprFunctionOp instances.
GitHub issue resolved #3253.
Pull request Description: Transforms now also affect Elements of ExprFunctionOp instances.

----

 - [x] Tests are included.
 - [x] Commits have been squashed to remove intermediate development commit messages.
 - [x] Key commit messages start with the issue number (GH-xxxx)

By submitting this pull request, I acknowledge that I am making a contribution to the Apache Software Foundation under the terms and conditions of the [Contributor's Agreement](https://www.apache.org/licenses/contributor-agreements.html).

----

See the [Apache Jena "Contributing" guide](https://github.com/apache/jena/blob/main/CONTRIBUTING.md).


Patch:
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;
 


Test patch:
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) ;
-        
+
     }
 }