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 377f9ad7fc Move SPARQL SameValue function to NodeFunctions
377f9ad7fc is described below

commit 377f9ad7fcbfd959efe9600b2a95b124258fd8c5
Author: Andy Seaborne <[email protected]>
AuthorDate: Sat Jan 10 14:36:11 2026 +0000

    Move SPARQL SameValue function to NodeFunctions
---
 .../org/apache/jena/sparql/expr/E_SameValue.java   | 22 +----
 .../org/apache/jena/sparql/expr/NVCompare.java     | 17 +++-
 .../jena/sparql/expr/nodevalue/NodeFunctions.java  | 84 +++++++++++++++---
 .../java/org/apache/jena/sparql/expr/TS_Expr.java  |  3 +-
 .../apache/jena/sparql/expr/TestNodeFunctions.java | 69 ---------------
 .../jena/sparql/expr/TestNodeFunctionsMisc.java    | 99 ++++++++++++++++++++++
 ...ctions.java => TestSPARQLKeywordFunctions.java} |  2 +-
 7 files changed, 189 insertions(+), 107 deletions(-)

diff --git 
a/jena-arq/src/main/java/org/apache/jena/sparql/expr/E_SameValue.java 
b/jena-arq/src/main/java/org/apache/jena/sparql/expr/E_SameValue.java
index 30f7530643..2739a273e7 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/expr/E_SameValue.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/expr/E_SameValue.java
@@ -18,6 +18,7 @@
 
 package org.apache.jena.sparql.expr;
 
+import org.apache.jena.sparql.expr.nodevalue.NodeFunctions;
 import org.apache.jena.sparql.sse.Tags;
 
 public class E_SameValue extends ExprFunction2 {
@@ -29,29 +30,12 @@ public class E_SameValue extends ExprFunction2 {
 
     @Override
     public NodeValue eval(NodeValue x, NodeValue y) {
-        if ( isNaN(x) ) {
-            if ( isNaN(y) )
-                return NodeValue.TRUE;
-            return NodeValue.FALSE;
-        }
-        boolean b = NodeValue.sameValueAs(x, y) ;
-        return NodeValue.booleanReturn(b) ;
+        return NodeFunctions.sameValueFunction(x, y);
     }
 
+
     @Override
     public Expr copy(Expr e1, Expr e2) {
         return new E_SameValue(e1, e2);
     }
-
-    private static boolean isNaN(NodeValue nv) {
-        if ( nv.isDouble() ) {
-            double d = nv.getDouble();
-            return Double.isNaN(d);
-        }
-        if ( nv.isFloat() ) {
-            float f = nv.getFloat();
-            return Float.isNaN(f);
-        }
-        return false;
-    }
 }
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/expr/NVCompare.java 
b/jena-arq/src/main/java/org/apache/jena/sparql/expr/NVCompare.java
index 16fc63d397..ea36410d26 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/expr/NVCompare.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/expr/NVCompare.java
@@ -114,8 +114,8 @@ class NVCompare {
                 Node node2 = nv2.asNode();
 
                 if ( !SystemARQ.ValueExtensions )
-                    // No value extensions => raw rdfTermEquals
-                    return NodeFunctions.rdfTermEquals(node1, node2);
+                    // No value extensions => raw SameValue, value testing 
done.
+                    raise(new ExprEvalException("Unknown equality test: " + 
nv1 + " and " + nv2));
 
                 // Some "value spaces" are know to be not equal (no overlap).
                 // Like one literal with a language tag, and one without can't 
be
@@ -127,12 +127,21 @@ class NVCompare {
                     return false;
 
                 // Two literals at this point.
+                // Any known to be disjoint value spaces.
 
                 if ( NodeFunctions.sameTerm(node1, node2) )
                     return true;
 
-                if ( !node1.getLiteralLanguage().equals("") || 
!node2.getLiteralLanguage().equals("") )
-                    // One had lang tag but weren't sameNode => not equals
+                boolean hasLang1 = NodeFunctions.hasLang(node1);
+                boolean hasLang2 = NodeFunctions.hasLang(node2);
+                if ( hasLang1 != hasLang2 )
+                    // One had lang tag, one doeesn't
+                    return false;
+
+                boolean hasLangDir1 = NodeFunctions.hasLangDir(node1);
+                boolean hasLangDir2 = NodeFunctions.hasLangDir(node2);
+                if ( hasLangDir1 != hasLangDir2 )
+                    // One had lang direction, one doesn't
                     return false;
 
                 raise(new ExprEvalException("Unknown equality test: " + nv1 + 
" and " + nv2));
diff --git 
a/jena-arq/src/main/java/org/apache/jena/sparql/expr/nodevalue/NodeFunctions.java
 
b/jena-arq/src/main/java/org/apache/jena/sparql/expr/nodevalue/NodeFunctions.java
index b1592ce414..c795cafb3d 100644
--- 
a/jena-arq/src/main/java/org/apache/jena/sparql/expr/nodevalue/NodeFunctions.java
+++ 
b/jena-arq/src/main/java/org/apache/jena/sparql/expr/nodevalue/NodeFunctions.java
@@ -59,15 +59,15 @@ public class NodeFunctions {
         return NodeValue.booleanReturn(sameTerm(nv1.asNode(), nv2.asNode()));
     }
 
-    // Jena - up to Jena 4.
-    // Language tags were kept in the case form they were given as.
-    // Jena5 - language tags are canonicalised.
-
     /** sameTerm(x,y) */
     public static boolean sameTerm(Node node1, Node node2) {
         return node1.sameTermAs(node2);
     }
 
+    // Jena - up to Jena 4.
+    // Language tags were kept in the case form they were given as.
+    // Jena5 - language tags are canonicalised.
+
 // Before jena stored normalized language tags ...
 //    /** sameTerm(x,y) */
 //    public static boolean sameTerm(Node node1, Node node2) {
@@ -94,44 +94,102 @@ public class NodeFunctions {
 
     // -------- sameValue
 
+    /** sameValue as equality */
     public static boolean sameValue(NodeValue nv1, NodeValue nv2) {
         return NodeValue.sameValueAs(nv1, nv2);
     }
 
+    /** sameValue as equality */
     public static boolean sameValue(Node node1, Node node2) {
         NodeValue nv1 = NodeValue.makeNode(node1);
         NodeValue nv2 = NodeValue.makeNode(node2);
         return NodeValue.sameValueAs(nv1, nv2);
     }
 
+    /** notSameValue as "not value-equals" */
     public static boolean notSameValue(NodeValue nv1, NodeValue nv2) {
         return NodeValue.notSameValueAs(nv1, nv2);
     }
 
+    /** notSameValue as "not value-equals" */
     public static boolean notSameValue(Node node1, Node node2) {
         NodeValue nv1 = NodeValue.makeNode(node1);
         NodeValue nv2 = NodeValue.makeNode(node2);
         return NodeValue.notSameValueAs(nv1, nv2);
     }
 
-    // -------- RDFterm-equals -- raises an exception on "don't know" for 
literals.
+    /**
+     * The sameValue function in SPARQL.
+     * NaN's do not follow value-equality.
+     */
+    public static NodeValue sameValueFunction(NodeValue x, NodeValue y) {
+        // Case: sameValue("NaN"^^xsd:double, "NaN"^^xsd:float) = true
+        if ( isNaN(x) )
+            return NodeValue.booleanReturn(isNaN(y));
+        else {
+            if ( isNaN(y) )
+                // x is not NaN
+                return NodeValue.FALSE;
+        }
+
+        // Case: sameValue("NaN"^^xsd:double, "NaN"^^xsd:float) = false
+        if ( false ) {
+            if ( isDoubleNaN(x) )
+                return NodeValue.booleanReturn(isDoubleNaN(y));
+            if ( isFloatNaN(x) )
+                return NodeValue.booleanReturn(isFloatNaN(y));
+            if (  isNaN(y) )
+                // x is not NaN
+                return NodeValue.FALSE;
+        }
+
+        boolean b = NodeValue.sameValueAs(x, y) ;
+        return NodeValue.booleanReturn(b) ;
+    }
+
+    /** Test whether the argument is a NaN, either as a double or as a float. 
*/
+    public static boolean isNaN(NodeValue nv) {
+        return isDoubleNaN(nv) || isFloatNaN(nv);
+    }
+
+    /** Test whether the argument is NaN as a double : "NaN"^^xsd:double.  */
+    public static boolean isDoubleNaN(NodeValue nv) {
+        if ( nv.isDouble() ) {
+            double d = nv.getDouble();
+            return Double.isNaN(d);
+        }
+        return false;
+    }
+
+    /** Test whether the argument is NaN as a float : "NaN"^^xsd:float.  */
+    public static boolean isFloatNaN(NodeValue nv) {
+        if ( nv.isFloat() ) {
+            float f = nv.getFloat();
+            return Float.isNaN(f);
+        }
+        return false;
+    }
+
 
-    // Exact as defined by SPARQL spec, when there are no value extensions.
-    //   Exception for two literals that might be equal but we don't know 
because of language tags.
+    // -------- RDFterm-equals -- raises an exception on "don't know" for 
literals; no value extensions.
 
+    // Exact as defined by SPARQL 1.1 spec, when there are no value extensions.
     // THIS IS NOT:
-    // SPARQL 1.1 = RDFterm-equals
     // SPARQL 1.2 = sameValue
 
-    public static boolean rdfTermEquals(Node n1, Node n2) {
+    // Legacy
+    @Deprecated(forRemoval = true)
+    public static boolean rdfTermEqual11_legacy(Node n1, Node n2) {
         if ( n1.equals(n2) )
             return true;
 
         if ( n1.isLiteral() && n2.isLiteral() ) {
+            // Jena4.
             // Two literals, may be sameTerm by language tag case 
insensitivity.
             String lang1 = n1.getLiteralLanguage();
             String lang2 = n2.getLiteralLanguage();
             if ( isNotEmpty(lang1) && isNotEmpty(lang2) ) {
+                // Jena4.
                 // Two language tags, both not "", equal by case insensitivity 
=> lexical test.
                 if ( lang1.equalsIgnoreCase(lang2) ) {
                     boolean b = 
n1.getLiteralLexicalForm().equals(n2.getLiteralLexicalForm());
@@ -141,7 +199,7 @@ public class NodeFunctions {
             }
 
             // Two literals:
-            //   Were not .equals
+            //   They were not .equals
             //   case 1: At least one language tag., not same lexical form -> 
unknown.
             //   case 2: No language tags, not .equals -> unknown.
             // Raise error (rather than return false).
@@ -151,9 +209,9 @@ public class NodeFunctions {
         if ( n1.isTripleTerm() && n2.isTripleTerm() ) {
             Triple t1 = n1.getTriple();
             Triple t2 = n2.getTriple();
-            return rdfTermEquals(t1.getSubject(), t2.getSubject())
-                && rdfTermEquals(t1.getPredicate(), t2.getPredicate())
-                && rdfTermEquals(t1.getObject(), t2.getObject());
+            return rdfTermEqual11_legacy(t1.getSubject(), t2.getSubject())
+                && rdfTermEqual11_legacy(t1.getPredicate(), t2.getPredicate())
+                && rdfTermEqual11_legacy(t1.getObject(), t2.getObject());
         }
 
         // Not both literal nor both triple terms - .equals would have worked.
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 1ef5eb3696..309a26e83d 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
@@ -35,10 +35,11 @@ import 
org.apache.jena.sparql.expr.nodevalue.TestNodeValueSortKey;
     , TestExpressions4.class
     , TestCastXSD.class
     , TestNodeFunctions.class
+    , TestNodeFunctionsMisc.class
     , TestExpressionsMath.class
     , TestFunctions.class
     , TestStringArgCompatibility.class
-    , TestSparqlKeywordFunctions.class
+    , TestSPARQLKeywordFunctions.class
     , TestFunctionsByURI.class
     , TestExprTripleTerms.class
     , TestLeviathanFunctions.class
diff --git 
a/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestNodeFunctions.java 
b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestNodeFunctions.java
index cc3ee94148..449071e7f1 100644
--- a/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestNodeFunctions.java
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestNodeFunctions.java
@@ -29,7 +29,6 @@ import org.apache.jena.graph.TextDirection;
 import org.apache.jena.query.ARQ;
 import org.apache.jena.sparql.expr.nodevalue.NodeFunctions;
 import org.apache.jena.sparql.graph.NodeConst;
-import org.apache.jena.sparql.sse.SSE;
 import org.apache.jena.vocabulary.XSD;
 
 public class TestNodeFunctions {
@@ -72,74 +71,6 @@ public class TestNodeFunctions {
         assertTrue(NodeFunctions.sameTerm(n1, n2));
     }
 
-    @Test public void testRDFtermEquals1() {
-        Node n1 = NodeFactory.createURI("xyz");
-        Node n2 = NodeFactory.createLiteralString("xyz");
-        assertFalse(NodeFunctions.rdfTermEquals(n1, n2));
-    }
-
-    @Test public void testRDFtermEquals2() {
-        Node n1 = NodeFactory.createLiteralLang("xyz", "en");
-        Node n2 = NodeFactory.createLiteralLang("xyz", "EN");
-        assertTrue(NodeFunctions.rdfTermEquals(n1, n2));
-    }
-
-    @Test
-    public void testRDFtermEquals3() {
-        // Unextended - not known to be same (no language tag support).
-        Node n1 = NodeFactory.createLiteralString("xyz");
-        Node n2 = NodeFactory.createLiteralLang("xyz", "en");
-        assertThrows(ExprEvalException.class,
-                     ()-> NodeFunctions.rdfTermEquals(n1, n2) );
-    }
-
-    @Test
-    public void testRDFtermEquals4() {
-        // Unextended - not known to be same.
-        Node n1 = NodeFactory.createLiteralDT("123", XSDDatatype.XSDinteger);
-        Node n2 = NodeFactory.createLiteralDT("456", XSDDatatype.XSDinteger);
-        assertThrows(ExprEvalException.class,
-                     ()-> assertTrue(NodeFunctions.rdfTermEquals(n1, n2)) );
-    }
-
-    @Test
-    public void testRDFtermEquals5() {
-        Node n1 = SSE.parseNode("<<(:s :p 123)>>");
-        Node n2 = SSE.parseNode("<<(:s :p 123)>>");
-        assertTrue(NodeFunctions.rdfTermEquals(n1, n2));
-    }
-
-    @Test
-    public void testRDFtermEquals6() {
-        Node n1 = SSE.parseNode("<<(:s :p1 123)>>");
-        Node n2 = SSE.parseNode("<<(:s :p2 123)>>");
-        assertFalse(NodeFunctions.rdfTermEquals(n1, n2));
-    }
-
-    @Test
-    public void testRDFtermEquals7() {
-        Node n1 = SSE.parseNode("<<(:s :p <<(:a :b 'abc')>>)>>");
-        Node n2 = SSE.parseNode("<<(:s :p <<(:a :b 123)>>)>>");
-        assertThrows(ExprEvalException.class,
-                     ()-> NodeFunctions.rdfTermEquals(n1, n2) );
-    }
-
-    @Test
-    public void testRDFtermEquals8() {
-        Node n1 = SSE.parseNode("<<(:s :p 123)>>");
-        Node n2 = SSE.parseNode("<<(:s :p 'xyz')>>");
-        assertThrows(ExprEvalException.class,
-                     ()-> assertFalse(NodeFunctions.rdfTermEquals(n2, n1)) );
-    }
-
-    @Test
-    public void testRDFtermEquals9() {
-        Node n1 = SSE.parseNode("<<(:s :p 123)>>");
-        Node n2 = SSE.parseNode("'xyz'");
-        assertFalse(NodeFunctions.rdfTermEquals(n1, n2));
-        assertFalse(NodeFunctions.rdfTermEquals(n2, n1));
-    }
-
     @Test public void testStr1() {
         NodeValue nv = NodeValue.makeNodeInteger(56);
         NodeValue s = NodeFunctions.str(nv);
diff --git 
a/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestNodeFunctionsMisc.java 
b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestNodeFunctionsMisc.java
new file mode 100644
index 0000000000..80465d7a88
--- /dev/null
+++ 
b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestNodeFunctionsMisc.java
@@ -0,0 +1,99 @@
+/*
+ * 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
+ *
+ *   https://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.
+ *
+ *   SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.apache.jena.sparql.expr;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+import org.apache.jena.datatypes.xsd.XSDDatatype;
+import org.apache.jena.graph.Node;
+import org.apache.jena.graph.NodeFactory;
+import org.apache.jena.sparql.expr.nodevalue.NodeFunctions;
+import org.apache.jena.sparql.sse.SSE;
+
+/**
+ * Tests for "RDFterm-equal" - fallback function for testing for the 
'='operator.
+ * Extended for triple terms.
+ * Replaced in SPARQL 1.2 by sameValue.
+ */
+@SuppressWarnings("removal")
+public class TestNodeFunctionsMisc {
+    @Test public void testRDFtermEquals1() {
+        Node n1 = NodeFactory.createURI("xyz");
+        Node n2 = NodeFactory.createLiteralString("xyz");
+        assertFalse(NodeFunctions.rdfTermEqual11_legacy(n1, n2));
+    }
+
+    @Test public void testRDFtermEquals2() {
+        Node n1 = NodeFactory.createLiteralLang("xyz", "en");
+        Node n2 = NodeFactory.createLiteralLang("xyz", "EN");
+        assertTrue(NodeFunctions.rdfTermEqual11_legacy(n1, n2));
+    }
+
+    @Test public void testRDFtermEquals3() {
+        // Unextended - not known to be same (no language tag support).
+        Node n1 = NodeFactory.createLiteralString("xyz");
+        Node n2 = NodeFactory.createLiteralLang("xyz", "en");
+        assertThrows(ExprEvalException.class, ()-> 
NodeFunctions.rdfTermEqual11_legacy(n1, n2) );
+    }
+
+    @Test public void testRDFtermEquals4() {
+        // Unextended - not known to be same.
+        Node n1 = NodeFactory.createLiteralDT("123", XSDDatatype.XSDinteger);
+        Node n2 = NodeFactory.createLiteralDT("456", XSDDatatype.XSDinteger);
+        assertThrows(ExprEvalException.class, ()-> 
assertTrue(NodeFunctions.rdfTermEqual11_legacy(n1, n2)) );
+    }
+
+    @Test public void testRDFtermEquals5() {
+        Node n1 = SSE.parseNode("<<(:s :p 123)>>");
+        Node n2 = SSE.parseNode("<<(:s :p 123)>>");
+        assertTrue(NodeFunctions.rdfTermEqual11_legacy(n1, n2));
+    }
+
+    @Test public void testRDFtermEquals6() {
+        Node n1 = SSE.parseNode("<<(:s :p1 123)>>");
+        Node n2 = SSE.parseNode("<<(:s :p2 123)>>");
+        assertFalse(NodeFunctions.rdfTermEqual11_legacy(n1, n2));
+    }
+
+    @Test public void testRDFtermEquals7() {
+        Node n1 = SSE.parseNode("<<(:s :p <<(:a :b 'abc')>>)>>");
+        Node n2 = SSE.parseNode("<<(:s :p <<(:a :b 123)>>)>>");
+        assertThrows(ExprEvalException.class, ()-> 
NodeFunctions.rdfTermEqual11_legacy(n1, n2) );
+    }
+
+    @Test public void testRDFtermEquals8() {
+        Node n1 = SSE.parseNode("<<(:s :p 123)>>");
+        Node n2 = SSE.parseNode("<<(:s :p 'xyz')>>");
+        assertThrows(ExprEvalException.class, ()-> 
assertFalse(NodeFunctions.rdfTermEqual11_legacy(n2, n1)) );
+    }
+
+    @Test public void testRDFtermEquals9() {
+        Node n1 = SSE.parseNode("<<(:s :p 123)>>");
+        Node n2 = SSE.parseNode("'xyz'");
+        assertFalse(NodeFunctions.rdfTermEqual11_legacy(n1, n2));
+        assertFalse(NodeFunctions.rdfTermEqual11_legacy(n2, n1));
+    }
+}
diff --git 
a/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestSparqlKeywordFunctions.java
 
b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestSPARQLKeywordFunctions.java
similarity index 99%
rename from 
jena-arq/src/test/java/org/apache/jena/sparql/expr/TestSparqlKeywordFunctions.java
rename to 
jena-arq/src/test/java/org/apache/jena/sparql/expr/TestSPARQLKeywordFunctions.java
index 25f2e6150d..177c4e54da 100644
--- 
a/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestSparqlKeywordFunctions.java
+++ 
b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestSPARQLKeywordFunctions.java
@@ -36,7 +36,7 @@ import org.apache.jena.sparql.util.ExprUtils;
 import org.apache.jena.sparql.util.NodeFactoryExtra;
 import org.apache.jena.sys.JenaSystem;
 
-public class TestSparqlKeywordFunctions
+public class TestSPARQLKeywordFunctions
 {
     static { JenaSystem.init(); }
     // Some overlap with TestFunctions except those are direct function calls 
and these are via SPARQL 1.1 syntax.

Reply via email to