Skip to content

Commit

Permalink
Merge pull request apache#1007 from afs/sparql-star
Browse files Browse the repository at this point in the history
JENA-2108: Compare, collate and sameTerm.
  • Loading branch information
afs authored May 24, 2021
2 parents 653f9f5 + 57596d7 commit 6a5cbe2
Show file tree
Hide file tree
Showing 7 changed files with 167 additions and 35 deletions.
66 changes: 46 additions & 20 deletions jena-arq/src/main/java/org/apache/jena/sparql/expr/NodeValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
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 ;
Expand Down Expand Up @@ -409,30 +410,26 @@ public Node evalNode(Binding binding, ExecutionContext execCxt)
}

@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

Expand All @@ -451,7 +448,7 @@ public static boolean sameAs(NodeValue nv1, NodeValue nv2)
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)
{
Expand Down Expand Up @@ -482,15 +479,15 @@ public static boolean sameAs(NodeValue nv1, NodeValue nv2)
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()) ;
Expand Down Expand Up @@ -539,6 +536,13 @@ public static boolean sameAs(NodeValue nv1, NodeValue nv2)
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
Expand Down Expand Up @@ -669,6 +673,7 @@ private static int compare(NodeValue nv1, NodeValue nv2, boolean sortOrderingCom
case VSPACE_BOOLEAN :
case VSPACE_DIFFERENT :
case VSPACE_LANG :
case VSPACE_TRIPLE_TERM:
case VSPACE_NODE :
case VSPACE_NUM :
case VSPACE_STRING :
Expand Down Expand Up @@ -752,6 +757,18 @@ private static int compare(NodeValue nv1, NodeValue nv2, boolean sortOrderingCom
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 )
Expand Down Expand Up @@ -789,6 +806,15 @@ private static int compare(NodeValue nv1, NodeValue nv2, boolean sortOrderingCom
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() ;
Expand All @@ -809,12 +835,13 @@ private static ValueSpaceClassification classifyValueSpace(NodeValue nv)
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 ;
Expand All @@ -827,7 +854,6 @@ private static ValueSpaceClassification classifyValueSpace(NodeValue nv)

if ( nv.isSortKey() ) return VSPACE_SORTKEY ;

// Already a literal by this point.
if ( NodeUtils.hasLang(nv.asNode()) )
return VSPACE_LANG ;
return VSPACE_UNKNOWN ;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,21 @@

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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
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;
Expand Down Expand Up @@ -120,6 +121,7 @@ public static NodeValue sameTerm(NodeValue nv1, NodeValue nv2) {
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 ;
Expand All @@ -130,9 +132,18 @@ public static boolean sameTerm(Node node1, Node node2) {
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.
Expand Down Expand Up @@ -162,7 +173,16 @@ public static boolean rdfTermEquals(Node n1, Node n2) {
// 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 ;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) ; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
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 ;
Expand Down Expand Up @@ -100,9 +101,46 @@ public void testRDFtermEquals4() {
// 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) ;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -820,7 +820,7 @@ public void testNodeSortKey2() {
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<>();
Expand All @@ -835,7 +835,7 @@ public int compare(NodeValue o1, NodeValue 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]));
Expand All @@ -846,7 +846,7 @@ public void testNodeSortKey3() {
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<>();
Expand All @@ -861,7 +861,7 @@ public int compare(NodeValue o1, NodeValue 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]));
Expand Down Expand Up @@ -1068,8 +1068,8 @@ public void testEquals3() { // Make different ways but equals

@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);
}

Expand All @@ -1093,4 +1093,36 @@ public void testNotEquals3() { // Literals and URIs are different.
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);
}

}
Loading

0 comments on commit 6a5cbe2

Please sign in to comment.