You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jena.apache.org by an...@apache.org on 2021/05/24 08:44:26 UTC

[jena] branch main updated: JENA-2108: Compare, collate and sameTerm.

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 57596d7  JENA-2108: Compare, collate and sameTerm.
     new 6a5cbe2  Merge pull request #1007 from afs/sparql-star
57596d7 is described below

commit 57596d7aeefadf0c24e1a8502451ceb917e964e4
Author: Andy Seaborne <an...@apache.org>
AuthorDate: Wed May 19 18:06:13 2021 +0100

    JENA-2108: Compare, collate and sameTerm.
---
 .../org/apache/jena/sparql/expr/NodeValue.java     | 66 +++++++++++++++-------
 .../jena/sparql/expr/ValueSpaceClassification.java | 12 ++--
 .../jena/sparql/expr/nodevalue/NodeFunctions.java  | 22 +++++++-
 .../apache/jena/sparql/expr/TestExpressions2.java  | 14 +++++
 .../apache/jena/sparql/expr/TestNodeFunctions.java | 38 +++++++++++++
 .../org/apache/jena/sparql/expr/TestNodeValue.java | 44 +++++++++++++--
 .../function/library/TestFnFunctionsCollation.java |  6 +-
 7 files changed, 167 insertions(+), 35 deletions(-)

diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/expr/NodeValue.java b/jena-arq/src/main/java/org/apache/jena/sparql/expr/NodeValue.java
index 844eed8..11c78c6 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/expr/NodeValue.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/expr/NodeValue.java
@@ -41,6 +41,7 @@ import org.apache.jena.datatypes.xsd.XSDDateTime ;
 import org.apache.jena.ext.xerces.DatatypeFactoryInst;
 import org.apache.jena.graph.Node ;
 import org.apache.jena.graph.NodeFactory ;
+import org.apache.jena.graph.Triple;
 import org.apache.jena.graph.impl.LiteralLabel ;
 import org.apache.jena.sparql.ARQInternalErrorException ;
 import org.apache.jena.sparql.SystemARQ ;
@@ -409,30 +410,26 @@ public abstract class NodeValue extends ExprNode
     }
 
     @Override
-    public boolean isConstant() { return true ; }
+    public boolean isConstant()     { return true ; }
 
     @Override
-    public NodeValue getConstant()     { return this ; }
+    public NodeValue getConstant()  { return this ; }
 
-    public boolean isIRI()
-    {
+    public boolean isIRI() {
         forceToNode() ;
         return node.isURI() ;
     }
 
-    public boolean isBlank()
-    {
+    public boolean isBlank() {
         forceToNode() ;
         return node.isBlank() ;
     }
 
-    public boolean isTripleTerm()
-    {
+    public boolean isTripleTerm() {
         forceToNode() ;
-        return node.isNodeTriple();
+        return node.isNodeTriple() ;
     }
 
-
     // ----------------------------------------------------------------
     // ---- sameValueAs
 
@@ -451,7 +448,7 @@ public abstract class NodeValue extends ExprNode
         ValueSpaceClassification compType = classifyValueOp(nv1, nv2) ;
 
         // Special case - date/dateTime comparison is affected by timezones and may be
-        // interdeterminate based on the value of the dateTime/date.
+        // indeterminate based on the value of the dateTime/date.
 
         switch (compType)
         {
@@ -482,15 +479,15 @@ public abstract class NodeValue extends ExprNode
             case VSPACE_STRING:     return XSDFuncOp.compareString(nv1, nv2) == Expr.CMP_EQUAL ;
             case VSPACE_BOOLEAN:    return XSDFuncOp.compareBoolean(nv1, nv2) == Expr.CMP_EQUAL ;
 
-            case VSPACE_LANG:
-            {
-                // two literals, both with a language tag
-                Node node1 = nv1.asNode() ;
-                Node node2 = nv2.asNode() ;
-                return node1.getLiteralLexicalForm().equals(node2.getLiteralLexicalForm()) &&
-                       node1.getLiteralLanguage().equalsIgnoreCase(node2.getLiteralLanguage()) ;
+            case VSPACE_TRIPLE_TERM: {
+                Triple t1 = nv1.getNode().getTriple();
+                Triple t2 = nv2.getNode().getTriple();
+                return nSameAs(t1.getSubject(), t2.getSubject())
+                    && nSameAs(t1.getPredicate(), t2.getPredicate())
+                    && nSameAs(t1.getObject(), t2.getObject());
             }
 
+            case VSPACE_LANG:
             case VSPACE_NODE:
                 // Two non-literals
                 return NodeFunctions.sameTerm(nv1.getNode(), nv2.getNode()) ;
@@ -539,6 +536,13 @@ public abstract class NodeValue extends ExprNode
         throw new ARQInternalErrorException("sameValueAs failure "+nv1+" and "+nv2) ;
     }
 
+    /** Worker for sameAs. */
+    private static boolean nSameAs(Node n1, Node n2) {
+        NodeValue nv1 = NodeValue.makeNode(n1);
+        NodeValue nv2 = NodeValue.makeNode(n2);
+        return sameAs(nv1, nv2);
+    }
+
     /** Return true if the two Nodes are known to be different,
      *  return false if the two Nodes are known to be the same,
      *  else throw ExprEvalException
@@ -669,6 +673,7 @@ public abstract class NodeValue extends ExprNode
             case VSPACE_BOOLEAN :
             case VSPACE_DIFFERENT :
             case VSPACE_LANG :
+            case VSPACE_TRIPLE_TERM:
             case VSPACE_NODE :
             case VSPACE_NUM :
             case VSPACE_STRING :
@@ -752,6 +757,18 @@ public abstract class NodeValue extends ExprNode
                 return x ;
             }
 
+            case VSPACE_TRIPLE_TERM: {
+                Triple t1 = nv1.getNode().getTriple();
+                Triple t2 = nv2.getNode().getTriple();
+                int x = nCompare(t1.getSubject(), t2.getSubject(), sortOrderingCompare);
+                if ( x != CMP_EQUAL )
+                    return x;
+                x = nCompare(t1.getPredicate(), t2.getPredicate(), sortOrderingCompare);
+                if ( x != CMP_EQUAL )
+                    return x;
+                return nCompare(t1.getObject(), t2.getObject(), sortOrderingCompare);
+            }
+
             case VSPACE_NODE:
                 // Two non-literals don't compare except for sorting.
                 if ( sortOrderingCompare )
@@ -789,6 +806,15 @@ public abstract class NodeValue extends ExprNode
         throw new ARQInternalErrorException("Compare failure "+nv1+" and "+nv2) ;
     }
 
+    /** Worker for compare. */
+    private static int nCompare(Node n1, Node n2, boolean sortOrderingCompare) {
+        if ( n1.equals(n2) )
+            return CMP_EQUAL;
+        NodeValue nv1 = NodeValue.makeNode(n1);
+        NodeValue nv2 = NodeValue.makeNode(n2);
+        return compare(nv1, nv2, sortOrderingCompare);
+    }
+
     public static ValueSpaceClassification classifyValueOp(NodeValue nv1, NodeValue nv2)
     {
         ValueSpaceClassification c1 = nv1.getValueSpace() ;
@@ -809,12 +835,13 @@ public abstract class NodeValue extends ExprNode
         if ( nv.isDateTime() )      return VSPACE_DATETIME ;
         if ( nv.isString())         return VSPACE_STRING ;
         if ( nv.isBoolean())        return VSPACE_BOOLEAN ;
+        if ( nv.isTripleTerm())     return VSPACE_TRIPLE_TERM ;
         if ( ! nv.isLiteral() )     return VSPACE_NODE ;
 
         if ( ! SystemARQ.ValueExtensions )
             return VSPACE_UNKNOWN ;
 
-        // Datatypes and their value spaces that are an extension of strict SPARQL.
+        // Datatypes and their value spaces that are an extension of minimal SPARQL 1.1
         if ( nv.isDate() )          return VSPACE_DATE ;
         if ( nv.isTime() )          return VSPACE_TIME ;
         if ( nv.isDuration() )      return VSPACE_DURATION ;
@@ -827,7 +854,6 @@ public abstract class NodeValue extends ExprNode
 
         if ( nv.isSortKey() )       return VSPACE_SORTKEY ;
 
-        // Already a literal by this point.
         if ( NodeUtils.hasLang(nv.asNode()) )
             return VSPACE_LANG ;
         return VSPACE_UNKNOWN ;
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/expr/ValueSpaceClassification.java b/jena-arq/src/main/java/org/apache/jena/sparql/expr/ValueSpaceClassification.java
index 218cdd0..e3c403f 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/expr/ValueSpaceClassification.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/expr/ValueSpaceClassification.java
@@ -20,19 +20,21 @@ package org.apache.jena.sparql.expr;
 
 public enum ValueSpaceClassification {
     VSPACE_NODE,
-    VSPACE_NUM, 
-    VSPACE_DATETIME, 
+    VSPACE_TRIPLE_TERM,
+
+    VSPACE_NUM,
+    VSPACE_DATETIME,
     VSPACE_DATE,
     VSPACE_TIME,
     VSPACE_DURATION,
-    
+
     // Collapse to VSPACE_DATETIME?
     VSPACE_G_YEAR,
     VSPACE_G_YEARMONTH,
     VSPACE_G_MONTHDAY,
-    VSPACE_G_MONTH,    
+    VSPACE_G_MONTH,
     VSPACE_G_DAY,
-    
+
     VSPACE_STRING, VSPACE_LANG, VSPACE_SORTKEY,
     VSPACE_BOOLEAN,
     VSPACE_UNKNOWN,
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 0eec5e1..2280a5a 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
@@ -31,6 +31,7 @@ import javax.xml.datatype.Duration;
 import org.apache.jena.datatypes.xsd.XSDDatatype ;
 import org.apache.jena.graph.Node ;
 import org.apache.jena.graph.NodeFactory ;
+import org.apache.jena.graph.Triple;
 import org.apache.jena.irix.IRIException;
 import org.apache.jena.irix.IRIs;
 import org.apache.jena.irix.IRIx;
@@ -120,6 +121,7 @@ public class NodeFunctions {
         return NodeValue.booleanReturn(sameTerm(nv1.asNode(), nv2.asNode())) ;
     }
 
+    /** sameTerm(x,y) */
     public static boolean sameTerm(Node node1, Node node2) {
         if ( node1.equals(node2) )
             return true ;
@@ -130,9 +132,18 @@ public class NodeFunctions {
                 return false;
             return node1.getLiteralLanguage().equalsIgnoreCase(node2.getLiteralLanguage());
         }
+        if ( node1.isNodeTriple() && node2.isNodeTriple() ) {
+            return sameTriples(node1.getTriple(), node2.getTriple());
+        }
         return false ;
     }
 
+    private static boolean sameTriples(Triple t1, Triple t2) {
+        return sameTerm(t1.getSubject(), t2.getSubject())
+            && sameTerm(t1.getPredicate(), t2.getPredicate())
+            && sameTerm(t1.getObject(), t2.getObject());
+    }
+
     // -------- RDFterm-equals -- raises an exception on "don't know" for literals.
 
     // Exact as defined by SPARQL spec, when there are no value extensions.
@@ -162,7 +173,16 @@ public class NodeFunctions {
             // Raise error (rather than return false).
             NodeValue.raise(new ExprEvalException("Mismatch in RDFterm-equals: " + n1 + ", " + n2)) ;
         }
-        // One or both not a literal.
+
+        if ( n1.isNodeTriple() && n2.isNodeTriple() ) {
+            Triple t1 = n1.getTriple();
+            Triple t2 = n2.getTriple();
+            return rdfTermEquals(t1.getSubject(), t2.getSubject())
+                && rdfTermEquals(t1.getPredicate(), t2.getPredicate())
+                && rdfTermEquals(t1.getObject(), t2.getObject());
+        }
+
+        // Not both literal nor both tripel terms - .equals would have worked.
         return false ;
     }
 
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestExpressions2.java b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestExpressions2.java
index 84021e5..bd6f0e0 100644
--- a/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestExpressions2.java
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestExpressions2.java
@@ -113,6 +113,20 @@ public class TestExpressions2
     @Test (expected=ExprEvalException.class)
     public void term_constructor_strlang_03()       { eval("STRLANG('abc'@en, 'en') = 'abc'@en") ; }
 
+    // RDF-star
+    @Test public void triple_term_cmp_01()
+    { eval("<<<ex:s> <ex:p> <ex:p>>> = <<<ex:s> <ex:p> <ex:p>>>"); }
+
+    @Test public void triple_term_cmp_02()
+    { eval("<<<ex:s> <ex:p> <ex:o1>>> != <<<ex:s> <ex:p> <ex:o2>>>"); }
+
+    @Test public void triple_term_cmp_03()
+    { eval("<<<ex:s> <ex:p> 1>> < <<<ex:s> <ex:p> 2>>"); }
+
+    @Test (expected=ExprEvalException.class)
+    public void triple_term_cmp_04()
+    { eval("<<<ex:s> <ex:p1> 2>> < <<<ex:s> <ex:p2> 2>>"); }
+
     // XSD casts
 
     @Test public void xsd_cast_01()                 { eval("xsd:integer('1') = 1", true) ; }
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 dcd00db..683bcde 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
@@ -28,6 +28,7 @@ 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.graph.NodeConst ;
+import org.apache.jena.sparql.sse.SSE;
 import org.apache.jena.vocabulary.RDF ;
 import org.apache.jena.vocabulary.XSD ;
 import org.junit.Test ;
@@ -100,9 +101,46 @@ public class TestNodeFunctions {
         // Unextended - not known to be same.
         Node n1 = NodeFactory.createLiteral("123", XSDDatatype.XSDinteger) ;
         Node n2 = NodeFactory.createLiteral("456", XSDDatatype.XSDinteger) ;
+        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(expected=ExprEvalException.class)
+    public void testRDFtermEquals7() {
+        Node n1 = SSE.parseNode("<<:s :p <<:a :b 'abc'>>>>");
+        Node n2 = SSE.parseNode("<<:s :p <<:a :b 123>>>>");
         NodeFunctions.rdfTermEquals(n1, n2);
     }
 
+    @Test(expected=ExprEvalException.class)
+    public void testRDFtermEquals8() {
+        Node n1 = SSE.parseNode("<<:s :p 123>>");
+        Node n2 = SSE.parseNode("<<:s :p 'xyz'>>");
+        assertFalse(NodeFunctions.rdfTermEquals(n1, n2));
+        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/TestNodeValue.java b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestNodeValue.java
index cba7db6..edb0e87 100644
--- a/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestNodeValue.java
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestNodeValue.java
@@ -820,7 +820,7 @@ public class TestNodeValue
         final String[] unordered =
                 {"Broager", "Åkirkeby", "Børkop", "Ærøskøbing", "Brædstrup", "Wandsbek"};
         final String[] ordered =
-                {"'Broager'", "'Brædstrup'", "'Børkop'", "'Wandsbek'", "'Ærøskøbing'", "'Åkirkeby'"};
+                {"Broager", "Brædstrup", "Børkop", "Wandsbek", "Ærøskøbing", "Åkirkeby"};
         // tests collation sort order for Danish
         final String collation = "da";
         List<NodeValue> nodeValues = new LinkedList<>();
@@ -835,7 +835,7 @@ public class TestNodeValue
         });
         List<String> result = new LinkedList<>();
         for (NodeValue nv : nodeValues) {
-            String s = nv.toString();
+            String s = nv.getNode().getLiteralLexicalForm();
             result.add(s);
         }
         assertArrayEquals(ordered, result.toArray(new String[0]));
@@ -846,7 +846,7 @@ public class TestNodeValue
         final String[] unordered = new String[]
                 {"Broager", "Åkirkeby", "Børkop", "Ærøskøbing", "Brædstrup", "Wandsbek"};
         final String[] ordered = new String[]
-                {"'Ærøskøbing'", "'Åkirkeby'", "'Brædstrup'", "'Broager'", "'Børkop'", "'Wandsbek'"};
+                {"Ærøskøbing", "Åkirkeby", "Brædstrup", "Broager", "Børkop", "Wandsbek"};
         // tests collation sort order with Danish words, but New Zealand English collation rules
         final String collation = "en-NZ";
         List<NodeValue> nodeValues = new LinkedList<>();
@@ -861,7 +861,7 @@ public class TestNodeValue
         });
         List<String> result = new LinkedList<>();
         for (NodeValue nv : nodeValues) {
-            String s = nv.toString();
+            String s = nv.getNode().getLiteralLexicalForm();
             result.add(s);
         }
         assertArrayEquals(ordered, result.toArray(new String[0]));
@@ -1068,8 +1068,8 @@ public class TestNodeValue
 
     @Test
     public void testEquals4() {
-        NodeValue nv1 = NodeValue.makeNode(org.apache.jena.graph.NodeFactory.createURI("http://example"));
-        NodeValue nv2 = NodeValue.makeNode(org.apache.jena.graph.NodeFactory.createURI("http://example"));
+        NodeValue nv1 = NodeValue.makeNode(NodeFactory.createURI("http://example"));
+        NodeValue nv2 = NodeValue.makeNode(NodeFactory.createURI("http://example"));
         assertEquals("Not NodeValue.equals()", nv1, nv2);
     }
 
@@ -1093,4 +1093,36 @@ public class TestNodeValue
         NodeValue nv2 = NodeValue.makeNode(org.apache.jena.graph.NodeFactory.createLiteral("http://example"));
         assertFalse("NodeValue.equals()", nv1.equals(nv2));
     }
+
+    @Test
+    public void testTripleTerms1() {
+        Node n1 = SSE.parseNode("<<:s :p 123>>");
+        Node n2 = SSE.parseNode("<<:s :p 456>>");
+        NodeValue nv1 = NodeValue.makeNode(n1);
+        NodeValue nv2 = NodeValue.makeNode(n2);
+        int xa = NodeValue.compare(nv1, nv2);
+        assertEquals(Expr.CMP_LESS, xa);
+        int xb = NodeValue.compare(nv2, nv1);
+        assertEquals(Expr.CMP_GREATER, xb);
+    }
+
+    @Test(expected=ExprNotComparableException.class)
+    public void testTripleTerms2() {
+        Node n1 = SSE.parseNode("<<:s :p 123>>");
+        Node n2 = SSE.parseNode("<<:s :p 'abc'>>");
+        NodeValue nv1 = NodeValue.makeNode(n1);
+        NodeValue nv2 = NodeValue.makeNode(n2);
+        NodeValue.compare(nv1, nv2);
+    }
+
+    @Test
+    public void testTripleTerms3() {
+        Node n1 = SSE.parseNode("<<:s :p 123>>");
+        Node n2 = SSE.parseNode("<<:s :p 'abc'>>");
+        NodeValue nv1 = NodeValue.makeNode(n1);
+        NodeValue nv2 = NodeValue.makeNode(n2);
+        int x = NodeValue.compareAlways(nv1, nv2);
+        assertEquals(Expr.CMP_LESS, x);
+    }
+
 }
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TestFnFunctionsCollation.java b/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TestFnFunctionsCollation.java
index 4b6a2c0..b80e2fa 100644
--- a/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TestFnFunctionsCollation.java
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TestFnFunctionsCollation.java
@@ -39,8 +39,8 @@ public class TestFnFunctionsCollation {
 
         final String[] unordered = {"tšekin kieli" , "tulun kieli", "töyhtöhyyppä",
                                     "tsahurin kieli", "tsahurin kieli", "tulun kieli"};
-        String[] ordered = {"'tsahurin kieli'", "'tsahurin kieli'", "'tšekin kieli'",
-                            "'tulun kieli'", "'tulun kieli'", "'töyhtöhyyppä'"};
+        String[] ordered = {"tsahurin kieli", "tsahurin kieli", "tšekin kieli",
+                            "tulun kieli", "tulun kieli", "töyhtöhyyppä"};
         // tests collation sort order with Danish words, but New Zealand English
         // collation rules
         List<NodeValue> nodeValues = new LinkedList<>();
@@ -50,7 +50,7 @@ public class TestFnFunctionsCollation {
         nodeValues.sort((NodeValue o1, NodeValue o2) -> NodeValue.compare(o1, o2));
         List<String> result = new LinkedList<>();
         for ( NodeValue nv : nodeValues ) {
-            String s = nv.toString();
+            String s = nv.getNode().getLiteralLexicalForm();
             result.add(s);
         }
         assertArrayEquals(ordered, result.toArray(new String[0]));