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 2026/01/08 09:10:16 UTC
(jena) 01/02: GH-3685: Update string functions for RDF 1.2 base direction
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
commit c829fba30a8de6dce8128dd97dac1033e8007a44
Author: Andy Seaborne <an...@apache.org>
AuthorDate: Mon Jan 5 21:34:55 2026 +0000
GH-3685: Update string functions for RDF 1.2 base direction
---
.../apache/jena/riot/writer/JsonLD11Writer.java | 9 +-
.../jena/sparql/expr/nodevalue/NodeValueOps.java | 84 ++--
.../jena/sparql/expr/nodevalue/XSDFuncOp.java | 82 ++--
.../org/apache/jena/arq/junit/OmittedTest.java | 4 +-
.../org/apache/jena/rdfs/TestDatasetGraphRDFS.java | 4 +-
.../java/org/apache/jena/sparql/expr/TS_Expr.java | 3 +-
.../org/apache/jena/sparql/expr/TestFunctions.java | 141 +-----
...tions2.java => TestSparqlKeywordFunctions.java} | 500 +++++++++++++--------
.../sparql/expr/TestStringArgCompatibility.java | 83 ++++
.../main/java/org/apache/jena/atlas/lib/Lib.java | 8 +-
.../org/apache/jena/fuseki/servlets/ActionLib.java | 4 +-
11 files changed, 507 insertions(+), 415 deletions(-)
diff --git a/jena-arq/src/main/java/org/apache/jena/riot/writer/JsonLD11Writer.java b/jena-arq/src/main/java/org/apache/jena/riot/writer/JsonLD11Writer.java
index 9b4d261474..40ce746117 100644
--- a/jena-arq/src/main/java/org/apache/jena/riot/writer/JsonLD11Writer.java
+++ b/jena-arq/src/main/java/org/apache/jena/riot/writer/JsonLD11Writer.java
@@ -18,6 +18,8 @@
package org.apache.jena.riot.writer;
+import static org.apache.jena.atlas.lib.Lib.equalsOrNulls;
+
import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;
@@ -37,7 +39,6 @@ import com.apicatalog.rdf.api.RdfConsumerException;
import jakarta.json.*;
import jakarta.json.stream.JsonGenerator;
-import org.apache.jena.atlas.lib.Lib;
import org.apache.jena.atlas.logging.FmtLog;
import org.apache.jena.graph.Node;
import org.apache.jena.riot.Lang;
@@ -96,11 +97,11 @@ public class JsonLD11Writer implements WriterDatasetRIOT {
boolean indented = true;
// Choose algorithms
- if ( Lib.equals(variant, RDFFormat.PRETTY) ) {
+ if ( equalsOrNulls(variant, RDFFormat.PRETTY) ) {
writeThis = writePretty(array, dsg);
- } else if ( variant == null || Lib.equals(variant, RDFFormat.PLAIN) ) {
+ } else if ( variant == null || equalsOrNulls(variant, RDFFormat.PLAIN) ) {
writeThis = writePlain(array, dsg);
- } else if ( Lib.equals(variant, RDFFormat.FLAT) ) {
+ } else if ( equalsOrNulls(variant, RDFFormat.FLAT) ) {
writeThis = writePlain(array, dsg);
indented = false;
} else {
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/expr/nodevalue/NodeValueOps.java b/jena-arq/src/main/java/org/apache/jena/sparql/expr/nodevalue/NodeValueOps.java
index 5b397efb6f..2a5534ee3f 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/expr/nodevalue/NodeValueOps.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/expr/nodevalue/NodeValueOps.java
@@ -27,14 +27,17 @@ import java.util.GregorianCalendar;
import javax.xml.datatype.Duration;
import javax.xml.datatype.XMLGregorianCalendar;
+import org.apache.jena.atlas.lib.Lib;
+import org.apache.jena.datatypes.RDFDatatype;
import org.apache.jena.datatypes.xsd.XSDDatatype;
import org.apache.jena.graph.Node;
import org.apache.jena.graph.NodeFactory;
+import org.apache.jena.graph.TextDirection;
import org.apache.jena.sparql.expr.ExprEvalException;
import org.apache.jena.sparql.expr.ExprEvalTypeException;
import org.apache.jena.sparql.expr.NodeValue;
import org.apache.jena.sparql.expr.ValueSpace;
-import org.apache.jena.sparql.util.NodeUtils;
+import org.apache.jena.vocabulary.RDF;
/**
* Operations relating to {@link NodeValue NodeValues}.
@@ -301,18 +304,36 @@ public class NodeValueOps {
Node n = nv.asNode();
if ( !n.isLiteral() )
throw new ExprEvalException(label + ": Not a literal: " + nv);
- String lang = n.getLiteralLanguage();
- if ( NodeUtils.isLangString(n) )
- // Language tag. Legal.
+ if ( nv.isString() )
+ // Includes derived types of xsd:string.
return n;
- // No language tag : either no datatype or a datatype of xsd:string
- // Includes the case of rdf:langString and no language ==> Illegal as a
- // compatible string.
+ RDFDatatype dt = n.getLiteralDatatype();
+ if ( ! RDF.dtLangString.equals(dt) && ! RDF.dtDirLangString.equals(dt) )
+ throw new ExprEvalException(label + ": Not a string literal: " + nv);
- if ( nv.isString() )
+ // Check for malformed:
+ // e.g. "abc"^^rdf:langString, and "abc"^^rdf:dirLangString
+
+ // Must have a language.
+ String lang = n.getLiteralLanguage();
+ if ( lang == null || lang.isEmpty() )
+ throw new ExprEvalException(label + ": Not a string literal (no langtag): " + nv);
+ if ( RDF.dtLangString.equals(dt) ) {
+ // Must not have a text direction
+ if ( n.getLiteralBaseDirection() != null )
+ throw new ExprEvalException(label + ": Not a string literal (rdf:langString + text direction): " + nv);
return n;
+ }
+ if ( RDF.dtDirLangString.equals(dt) ) {
+ // Must have a text direction
+ if ( n.getLiteralBaseDirection() == null )
+ throw new ExprEvalException(label + ": Not a string literal (no text direction): " + nv);
+ return n;
+ }
+
+ // Should not get here.
throw new ExprEvalException(label + ": Not a string literal: " + nv);
}
@@ -322,19 +343,36 @@ public class NodeValueOps {
* is not symmetric.
* <ul>
* <li>"abc"@en is compatible with "abc"
+ * <li>"abc"@en--ltr is compatible with "abc"
* <li>"abc" is NOT compatible with "abc"@en
+ * <li>"abc"@en--ltr is NOT compatible with "abc"@en
* </ul>
*/
public static void checkTwoArgumentStringLiterals(String label, NodeValue arg1, NodeValue arg2) {
- /* Quote the spec:
- * Compatibility of two arguments is defined as:
- * The arguments are simple literals or literals typed as xsd:string
- * The arguments are plain literals with identical language tags
- * The first argument is a plain literal with language tag and the second argument is a simple literal or literal typed as xsd:string
+ /* Compatibility of two arguments:
+ * The arguments are both xsd:string
+ * The arguments are rdf:langString with identical language tags
+ * The arguments are rdf:dirLangString with identical language tags and text direction
+ * The first argument a string literal (rdf:langString, rdf:dirLangString) and the second argument is an xsd:string
+ *
+ * which simplifies to
+ * Both arguments are string literals
+ * The second argument is an xsd:string.
+ * The first and second arguments have the same lang and text direction.
*/
+ // Common case
+ if ( arg1.isString() && arg2.isString() )
+ // Includes derived datatypes of xsd:string.
+ return;
+
+ // Robust checking.
Node n1 = checkAndGetStringLiteral(label, arg1);
Node n2 = checkAndGetStringLiteral(label, arg2);
+ if ( arg2.isString() )
+ // args1 is some kind of string literal.
+ return;
+ // same lane, same text direction.
String lang1 = n1.getLiteralLanguage();
String lang2 = n2.getLiteralLanguage();
if ( lang1 == null )
@@ -342,21 +380,13 @@ public class NodeValueOps {
if ( lang2 == null )
lang2 = "";
- // Case 1
- if ( lang1.equals("") ) {
- if ( lang2.equals("") )
- return;
+ if ( ! Lib.equalsOrNulls(lang1, lang2) )
+ // Different languages.
throw new ExprEvalException(label + ": Incompatible: " + arg1 + " and " + arg2);
- }
- // Case 2
- if ( lang1.equalsIgnoreCase(lang2) )
- return;
-
- // Case 3
- if ( lang2.equals("") )
- return;
-
- throw new ExprEvalException(label + ": Incompatible: " + arg1 + " and " + arg2);
+ TextDirection textDir1 = n1.getLiteralBaseDirection();
+ TextDirection textDir2 = n2.getLiteralBaseDirection();
+ if ( ! Lib.equalsOrNulls(textDir1, textDir2) )
+ throw new ExprEvalException(label + ": Incompatible: " + arg1 + " and " + arg2);
}
}
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/expr/nodevalue/XSDFuncOp.java b/jena-arq/src/main/java/org/apache/jena/sparql/expr/nodevalue/XSDFuncOp.java
index 6fd848817d..aef4a5ecc5 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/expr/nodevalue/XSDFuncOp.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/expr/nodevalue/XSDFuncOp.java
@@ -42,11 +42,13 @@ import javax.xml.datatype.Duration;
import javax.xml.datatype.XMLGregorianCalendar;
import org.apache.jena.atlas.lib.IRILib;
+import org.apache.jena.atlas.lib.Lib;
import org.apache.jena.atlas.lib.StrUtils;
import org.apache.jena.datatypes.RDFDatatype;
import org.apache.jena.datatypes.xsd.XSDDatatype;
import org.apache.jena.graph.Node;
import org.apache.jena.graph.NodeFactory;
+import org.apache.jena.graph.TextDirection;
import org.apache.jena.rdf.model.impl.Util;
import org.apache.jena.sparql.ARQInternalErrorException;
import org.apache.jena.sparql.SystemARQ;
@@ -745,7 +747,7 @@ public class XSDFuncOp
/** Build a NodeValue with lexical form, and same language and datatype as the Node argument */
private static NodeValue calcReturn(String result, Node arg) {
- Node n2 = NodeFactory.createLiteral(result, arg.getLiteralLanguage(), arg.getLiteralDatatype());
+ Node n2 = NodeFactory.createLiteral(result, arg.getLiteralLanguage(), arg.getLiteralBaseDirection(), arg.getLiteralDatatype());
return NodeValue.makeNode(n2);
}
@@ -823,57 +825,57 @@ public class XSDFuncOp
}
/** SPARQL CONCAT (no implicit casts to strings) */
- public static NodeValue strConcat(List<NodeValue> args) {
- // Step 1 : Choose type.
- // One lang tag -> that lang tag
- String lang = null;
- boolean mixedLang = false;
- boolean xsdString = false;
- boolean simpleLiteral = false;
+ public static NodeValue /*XSDFuncOp*/strConcat(List<NodeValue> args) {
+ // Is this list of argument known to result in xsd:string by being mixed or seen an xsd:string?
+ boolean outputSimpleString = false;
+
+ // A candidate has been set (happens at first argument)
+ boolean candidateSet = false;
+ String concatLang = null;
+ TextDirection concatTextDir = null;
StringBuilder sb = new StringBuilder();
for (NodeValue nv : args) {
+ // ExprEvalException is is not a string/langString or dirLangString.
Node n = NodeValueOps.checkAndGetStringLiteral("CONCAT", nv);
- String lang1 = n.getLiteralLanguage();
- if ( !lang1.equals("") ) {
- if ( lang != null && !lang1.equals(lang) )
- // throw new
- // ExprEvalException("CONCAT: Mixed language tags: "+args);
- mixedLang = true;
- lang = lang1;
- } else if ( n.getLiteralDatatype() != null )
- xsdString = true;
- else
- simpleLiteral = true;
-
sb.append(n.getLiteralLexicalForm());
+ if ( outputSimpleString )
+ continue;
+ String nLang = n.getLiteralLanguage();
+ TextDirection nTextDir = n.getLiteralBaseDirection();
+
+ // Not mixed,
+ if ( ! candidateSet ) {
+ if ( nLang.isEmpty() ) {
+ // No language => outcome is an xsd:string
+ outputSimpleString = nLang.isEmpty();
+ continue;
+ }
+ // It is the first argument, then set candidate
+ concatLang = nLang;
+ concatTextDir = nTextDir;
+ candidateSet = true;
+ continue;
+ }
+ // candidateSet is true
+ // Possible lang/textDir
+ if ( ! Lib.equalsOrNulls(concatLang, nLang) || ! Lib.equalsOrNulls(concatTextDir, nTextDir) ) {
+ // Different => outcome is an xsd:string
+ outputSimpleString = true;
+ candidateSet = false;
+ }
}
- if ( mixedLang )
- return NodeValue.makeString(sb.toString());
+ String string = sb.toString();
- // Must be all one lang.
- if ( lang != null ) {
- if ( !xsdString && !simpleLiteral )
- return NodeValue.makeNode(sb.toString(), lang, (String)null);
- else
- // Lang and one or more of xsd:string or simpleLiteral.
- return NodeValue.makeString(sb.toString());
- }
-
- if ( simpleLiteral && xsdString )
- return NodeValue.makeString(sb.toString());
- // All xsdString
- if ( xsdString )
- return NodeValue.makeNode(sb.toString(), XSDDatatype.XSDstring);
- if ( simpleLiteral )
+ if ( outputSimpleString )
return NodeValue.makeString(sb.toString());
- // No types - i.e. no arguments
- return NodeValue.makeString(sb.toString());
+ // Handles "textDir == null"
+ Node nOut = NodeFactory.createLiteralDirLang(string, concatLang, concatTextDir);
+ return NodeValue.makeNode(nOut);
}
-
/** fn:normalizeSpace */
public static NodeValue strNormalizeSpace(NodeValue v){
String str = v.asString();
diff --git a/jena-arq/src/test/java/org/apache/jena/arq/junit/OmittedTest.java b/jena-arq/src/test/java/org/apache/jena/arq/junit/OmittedTest.java
index 0dd1098c44..c21a3ff273 100644
--- a/jena-arq/src/test/java/org/apache/jena/arq/junit/OmittedTest.java
+++ b/jena-arq/src/test/java/org/apache/jena/arq/junit/OmittedTest.java
@@ -21,7 +21,7 @@ package org.apache.jena.arq.junit;
import org.apache.jena.arq.junit.manifest.ManifestEntry;
/**
- * Omitted test -= no marked as "suppressed" but not run.
+ * Omitted test = not marked as "suppressed" but not run.
*/
public class OmittedTest extends SkipTest {
public final boolean verbose;
@@ -38,7 +38,7 @@ public class OmittedTest extends SkipTest {
@Override
public void runTest() {
- //if ( verbose )
+ if ( verbose )
System.err.println("Omitted test: " + manifestEntry.getName());
}
}
diff --git a/jena-arq/src/test/java/org/apache/jena/rdfs/TestDatasetGraphRDFS.java b/jena-arq/src/test/java/org/apache/jena/rdfs/TestDatasetGraphRDFS.java
index 095a366c3e..6588923d2c 100644
--- a/jena-arq/src/test/java/org/apache/jena/rdfs/TestDatasetGraphRDFS.java
+++ b/jena-arq/src/test/java/org/apache/jena/rdfs/TestDatasetGraphRDFS.java
@@ -19,6 +19,7 @@
package org.apache.jena.rdfs;
import static org.apache.jena.atlas.iterator.Iter.iter;
+import static org.apache.jena.atlas.lib.Lib.equalsOrNulls;
import static org.apache.jena.graph.Node.ANY;
import static org.apache.jena.rdfs.LibTestRDFS.node;
import static org.apache.jena.rdfs.engine.ConstRDFS.rdfType;
@@ -36,7 +37,6 @@ import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.apache.jena.atlas.iterator.Iter;
-import org.apache.jena.atlas.lib.Lib;
import org.apache.jena.atlas.lib.ListUtils;
import org.apache.jena.atlas.lib.StrUtils;
import org.apache.jena.graph.Graph;
@@ -136,7 +136,7 @@ public class TestDatasetGraphRDFS {
}
private static boolean hasNG(List<Quad> quads, Node graphName) {
- return quads.stream().map(Quad::getGraph).anyMatch(gn -> Lib.equals(gn, graphName));
+ return quads.stream().map(Quad::getGraph).anyMatch(gn -> equalsOrNulls(gn, graphName));
}
private void testContains(Node g, Node s, Node p, Node o, boolean expected) {
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 06efb7eee7..1ef5eb3696 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
@@ -37,7 +37,8 @@ import org.apache.jena.sparql.expr.nodevalue.TestNodeValueSortKey;
, TestNodeFunctions.class
, TestExpressionsMath.class
, TestFunctions.class
- , TestFunctions2.class
+ , TestStringArgCompatibility.class
+ , TestSparqlKeywordFunctions.class
, TestFunctionsByURI.class
, TestExprTripleTerms.class
, TestLeviathanFunctions.class
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestFunctions.java b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestFunctions.java
index df04ffe1cf..fa873d2997 100644
--- a/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestFunctions.java
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestFunctions.java
@@ -19,7 +19,6 @@
package org.apache.jena.sparql.expr;
import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
@@ -31,15 +30,13 @@ import java.util.function.Predicate;
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.ARQConstants;
import org.apache.jena.sparql.util.ExprUtils;
public class TestFunctions
{
// Test of fn;* are in org.apache.jena.sparql.function.library.*
+ // TestFunctions2 has the SPARQl keyword functions.
private static final NodeValue TRUE = NodeValue.TRUE;
private static final NodeValue FALSE = NodeValue.FALSE;
@@ -121,150 +118,14 @@ public class TestFunctions
// Timezone +11:00 can be a day behind
@Test public void exprSprintf_25() { test_exprSprintf_tz_possibilites("2005-10-14T10:09:43+11:00", "10 13,2005", "10 14,2005"); }
- @Test public void exprStrStart10() { test("STRSTARTS('abc', 'abcd')", FALSE); }
- @Test public void exprStrStart11() { test("STRSTARTS('abc'@en, 'ab')", TRUE); }
- @Test public void exprStrStart12() { test("STRSTARTS('abc'^^xsd:string, 'ab')", TRUE); }
- @Test public void exprStrStart13() { test("STRSTARTS('abc'^^xsd:string, 'ab'^^xsd:string)", TRUE); }
- @Test public void exprStrStart14() { test("STRSTARTS('abc', 'ab'^^xsd:string)", TRUE); }
- @Test public void exprStrStart15() { test("STRSTARTS('abc'@en, 'ab'@en)", TRUE); }
-
- @Test public void exprStrStart16() { testEvalException("STRSTARTS('ab'@en, 'ab'@fr)"); }
- @Test public void exprStrStart17() { testEvalException("STRSTARTS(123, 'ab'@fr)"); }
- @Test public void exprStrStart18() { testEvalException("STRSTARTS('123'^^xsd:string, 12.3)"); }
-
- @Test public void exprStrBefore0() { test("STRBEFORE('abc', 'abcd')", NodeValue.nvEmptyString); }
- @Test public void exprStrBefore1() { test("STRBEFORE('abc'@en, 'b')", NodeValue.makeNode("a", "en", (String)null)); }
- @Test public void exprStrBefore2() { test("STRBEFORE('abc'^^xsd:string, 'c')", NodeValue.makeNode("ab", XSDDatatype.XSDstring)); }
- @Test public void exprStrBefore3() { test("STRBEFORE('abc'^^xsd:string, ''^^xsd:string)", NodeValue.makeNode("", XSDDatatype.XSDstring)); }
- @Test public void exprStrBefore4() { test("STRBEFORE('abc', 'ab'^^xsd:string)", NodeValue.nvEmptyString); }
- @Test public void exprStrBefore5() { test("STRBEFORE('abc'@en, 'b'@en)", NodeValue.makeNode("a", "en", (String)null)); }
-
- @Test public void exprStrBefore6() { testEvalException("STRBEFORE('ab'@en, 'ab'@fr)"); }
- @Test public void exprStrBefore7() { testEvalException("STRBEFORE(123, 'ab'@fr)"); }
- @Test public void exprStrBefore8() { testEvalException("STRBEFORE('123'^^xsd:string, 12.3)"); }
- // No match case
- @Test public void exprStrBefore9() { test("STRBEFORE('abc'^^xsd:string, 'z')", NodeValue.nvEmptyString); }
- // Empty string case
- @Test public void exprStrBefore10() { test("STRBEFORE('abc'^^xsd:string, '')", NodeValue.makeNode("", XSDDatatype.XSDstring)); }
-
- @Test public void exprStrAfter0() { test("STRAFTER('abc', 'abcd')", NodeValue.nvEmptyString); }
- @Test public void exprStrAfter1() { test("STRAFTER('abc'@en, 'b')", NodeValue.makeNode("c", "en", (String)null)); }
- @Test public void exprStrAfter2() { test("STRAFTER('abc'^^xsd:string, 'a')", NodeValue.makeNode("bc", XSDDatatype.XSDstring)); }
- @Test public void exprStrAfter3() { test("STRAFTER('abc'^^xsd:string, ''^^xsd:string)", NodeValue.makeNode("abc", XSDDatatype.XSDstring)); }
- @Test public void exprStrAfter4() { test("STRAFTER('abc', 'bc'^^xsd:string)", NodeValue.nvEmptyString); }
- @Test public void exprStrAfter5() { test("STRAFTER('abc'@en, 'b'@en)", NodeValue.makeNode("c", "en", (String)null)); }
-
- @Test public void exprStrAfter6() { testEvalException("STRAFTER('ab'@en, 'ab'@fr)"); }
- @Test public void exprStrAfter7() { testEvalException("STRAFTER(123, 'ab'@fr)"); }
- @Test public void exprStrAfter8() { testEvalException("STRAFTER('123'^^xsd:string, 12.3)"); }
- // No match case
- @Test public void exprStrAfter9() { test("STRAFTER('abc'^^xsd:string, 'z')", NodeValue.nvEmptyString); }
- // Empty string case
- @Test public void exprStrAfter10() { test("STRAFTER('abc'^^xsd:string, '')", NodeValue.makeNode("abc", XSDDatatype.XSDstring)); }
-
- @Test public void exprStrEnds10() { test("STRENDS('abc', 'abcd')", FALSE); }
- @Test public void exprStrEnds11() { test("STRENDS('abc'@en, 'bc')", TRUE); }
- @Test public void exprStrEnds12() { test("STRENDS('abc'^^xsd:string, 'c')", TRUE); }
- @Test public void exprStrEnds13() { test("STRENDS('abc'^^xsd:string, 'c'^^xsd:string)", TRUE); }
- @Test public void exprStrEnds14() { test("STRENDS('abc', 'ab'^^xsd:string)", FALSE); }
- @Test public void exprStrEnds15() { test("STRENDS('abc'@en, 'abc'@en)", TRUE); }
-
- @Test public void exprStrEnds16() { testEvalException("STRENDS('ab'@en, 'ab'@fr)"); }
- @Test public void exprStrEnds17() { testEvalException("STRENDS(123, 'ab'@fr)"); }
- @Test public void exprStrEnds18() { testEvalException("STRENDS('123'^^xsd:string, 12.3)"); }
-
- @Test public void exprContains10() { test("Contains('abc', 'abcd')", FALSE); }
- @Test public void exprContains11() { test("Contains('abc'@en, 'bc')", TRUE); }
- @Test public void exprContains12() { test("Contains('abc'^^xsd:string, 'c')", TRUE); }
- @Test public void exprContains13() { test("Contains('abc'^^xsd:string, 'c'^^xsd:string)", TRUE); }
- @Test public void exprContains14() { test("Contains('abc', 'z'^^xsd:string)", FALSE); }
- @Test public void exprContains15() { test("Contains('abc'@en, 'abc'@en)", TRUE); }
-
- @Test public void exprContains16() { testEvalException("Contains('ab'@en, 'ab'@fr)"); }
- @Test public void exprContains17() { testEvalException("Contains(123, 'ab'@fr)"); }
- @Test public void exprContains18() { testEvalException("STRENDS('123'^^xsd:string, 12.3)"); }
-
- @Test public void exprReplace01() { test("REPLACE('abc', 'b', 'Z')", NodeValue.makeString("aZc")); }
- @Test public void exprReplace02() { test("REPLACE('abc', 'b.', 'Z')", NodeValue.makeString("aZ")); }
- @Test public void exprReplace03() { test("REPLACE('abcbd', 'b.', 'Z')", NodeValue.makeString("aZZ")); }
-
- @Test public void exprReplace04() { test("REPLACE('abcbd'^^xsd:string, 'b.', 'Z')", NodeValue.makeNode("aZZ", XSDDatatype.XSDstring)); }
- @Test public void exprReplace05() { test("REPLACE('abcbd'@en, 'b.', 'Z')", NodeValue.makeNode("aZZ", "en", (String)null)); }
- @Test public void exprReplace06() { test("REPLACE('abcbd', 'B.', 'Z', 'i')", NodeValue.makeString("aZZ")); }
-
- // See JENA-740
- // ARQ provides replacement of the potentially empty string.
- @Test public void exprReplace07() { test("REPLACE('abc', '.*', 'Z')", NodeValue.makeString("Z")); }
- @Test public void exprReplace08() { test("REPLACE('', '.*', 'Z')", NodeValue.makeString("Z")); }
- @Test public void exprReplace09() { test("REPLACE('abc', '.?', 'Z')", NodeValue.makeString("ZZZ")); }
-
- @Test public void exprReplace10() { test("REPLACE('abc', 'XXX', 'Z')", NodeValue.makeString("abc")); }
- @Test public void exprReplace11() { test("REPLACE('', '.', 'Z')", NodeValue.makeString("")); }
- @Test public void exprReplace12() { test("REPLACE('', '(a|b)?', 'Z')", NodeValue.makeString("Z")); }
-
- // Bad group
- @Test
- public void exprReplace13() {
- testEvalException("REPLACE('abc', '.*', '$1')");
- }
-
- // Bad pattern; static (parse or build time) compilation.
- @Test
- public void exprReplace14() {
- assertThrows(ExprException.class,
- ()-> ExprUtils.parse("REPLACE('abc', '^(a){-9}', 'ABC')")
- );
- }
-
// Better name!
@Test public void localTimezone_2() { test("afn:timezone()", nv->nv.isDayTimeDuration()); }
-
@Test public void localDateTime_1() { test("afn:nowtz()", nv-> nv.isDateTime()); }
// Test field defined.
@Test public void localDateTime_2() { test("afn:nowtz()", nv-> nv.getDateTime().getTimezone() >= -14 * 60 ); }
@Test public void localDateTime_3() { test("afn:nowtz() = NOW()", NodeValue.TRUE); }
- @Test public void exprSameTerm1() { test("sameTerm(1,1)", TRUE); }
- @Test public void exprSameTerm2() { test("sameTerm(1,1.0)", FALSE); }
- @Test public void exprSameTerm3() { test("sameTerm(1,1e0)", FALSE); }
- @Test public void exprSameTerm4() { test("sameTerm(<_:a>, <_:a>)", TRUE); }
- @Test public void exprSameTerm5() { test("sameTerm(<x>, <x>)", TRUE); }
- @Test public void exprSameTerm6() { test("sameTerm(<x>, <y>)", FALSE); }
-
- @Test public void exprOneOf_01() { test("57 in (xsd:integer, '123')", FALSE); }
- @Test public void exprOneOf_02() { test("57 in (57)", TRUE); }
- @Test public void exprOneOf_03() { test("57 in (123, 57)", TRUE); }
- @Test public void exprOneOf_04() { test("57 in (57, 456)", TRUE); }
- @Test public void exprOneOf_05() { test("57 in (123, 57, 456)", TRUE); }
- @Test public void exprOneOf_06() { test("57 in (1,2,3)", FALSE); }
-
- @Test public void exprNotOneOf_01() { test("57 not in (xsd:integer, '123')", TRUE); }
- @Test public void exprNotOneOf_02() { test("57 not in (57)", FALSE); }
- @Test public void exprNotOneOf_03() { test("57 not in (123, 57)", FALSE); }
- @Test public void exprNotOneOf_04() { test("57 not in (57, 456)", FALSE); }
- @Test public void exprNotOneOf_05() { test("57 not in (123, 57, 456)", FALSE); }
- @Test public void exprNotOneOf_06() { test("57 not in (1,2,3)", TRUE); }
-
-
- static Node xyz_en = NodeFactory.createLiteralLang("xyz", "en");
- static NodeValue nv_xyz_en = NodeValue.makeNode(xyz_en);
-
- static Node xyz_xsd_string = NodeFactory.createLiteralDT("xyz", XSDDatatype.XSDstring);
- static NodeValue nv_xyz_string = NodeValue.makeNode(xyz_xsd_string);
-
-
- @Test public void exprStrLang1() { test("strlang('xyz', 'en')", nv_xyz_en); }
- @Test
- public void exprStrLang2() { assertThrows(ExprEvalException.class, ()-> test("strlang('xyz', '')", x->false) ); }
-
- @Test public void exprStrDatatype1() { test("strdt('123', xsd:integer)", NodeValue.makeInteger(123)); }
- @Test public void exprStrDatatype2() { test("strdt('xyz', xsd:string)", nv_xyz_string); }
- @Test public void exprStrDatatype3() { testEvalException("strdt('123', 'datatype')"); }
-
- static Node n_uri = NodeFactory.createURI("http://example/");
- static NodeValue nv_uri = NodeValue.makeNode(n_uri);
-
private void test(String exprStr, NodeValue result) {
Expr expr = ExprUtils.parse(exprStr);
NodeValue r = expr.eval(null, LibTestExpr.createTest());
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestFunctions2.java b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestSparqlKeywordFunctions.java
similarity index 53%
rename from jena-arq/src/test/java/org/apache/jena/sparql/expr/TestFunctions2.java
rename to jena-arq/src/test/java/org/apache/jena/sparql/expr/TestSparqlKeywordFunctions.java
index 44b5ef44e1..b4b06b6362 100644
--- a/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestFunctions2.java
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestSparqlKeywordFunctions.java
@@ -21,6 +21,7 @@ package org.apache.jena.sparql.expr;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
@@ -35,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 TestFunctions2
+public class TestSparqlKeywordFunctions
{
static { JenaSystem.init(); }
// Some overlap with TestFunctions except those are direct function calls and these are via SPARQL 1.1 syntax.
@@ -43,16 +44,21 @@ public class TestFunctions2
static boolean warnOnBadLexicalForms = true;
- @BeforeAll public static void beforeClass()
- {
+ @BeforeAll
+ public static void beforeClass() {
warnOnBadLexicalForms = NodeValue.VerboseWarnings;
NodeValue.VerboseWarnings = false;
}
- @AfterAll public static void afterClass()
- {
+
+ @AfterAll
+ public static void afterClass() {
NodeValue.VerboseWarnings = warnOnBadLexicalForms;
}
+ private final static String kwTRUE = "true";
+ private final static String kwFALSE = "false";
+ private final static String kwEmptyString = "''";;
+
// tests for strings. strlen, substr, strucase, strlcase, contains, concat
// Some overlap with NodeFunctions.
// Tests for IRI(..) are in TestUpdateOperations.insert_with_iri_function_resolution*
@@ -105,6 +111,7 @@ public class TestFunctions2
@Test public void round_13() { test("round('-0'^^xsd:float)", "'-0.0'^^xsd:float"); }
@Test public void round_14() { test("round('-0'^^xsd:double)", "'-0.0e0'^^xsd:double"); }
+ // ABS
@Test public void abs_01() { test("abs(1)", "1"); }
@Test public void abs_02() { test("abs(1.0)", "1.0"); }
@Test public void abs_03() { test("abs(1.0e0)", "1.0e0"); }
@@ -132,34 +139,40 @@ public class TestFunctions2
@Test public void floor_07() { test("floor(-9.5)", "'-10.0'^^xsd:decimal"); }
@Test public void floor_08() { test("floor(0)", "0"); }
-
// CONCAT
- @Test public void concat_00() { test("concat()", "''"); }
+ @Test public void concat_00() { test("concat()", kwEmptyString); }
@Test public void concat_01() { test("concat('a')", "'a'"); }
@Test public void concat_02() { test("concat('a', 'b')", "'ab'"); }
@Test public void concat_03() { test("concat('a'@en, 'b')", "'ab'"); }
@Test public void concat_04() { test("concat('a'@en, 'b'@en)", "'ab'@en"); }
- //@Test public void concat_05() { test("concat('a'^^xsd:string, 'b')", "'ab'^^xsd:string"); }
- @Test public void concat_05() { test("concat('a'^^xsd:string, 'b')", "'ab'"); }
- @Test public void concat_06() { test("concat('a'^^xsd:string, 'b'^^xsd:string)", "'ab'^^xsd:string"); }
+ @Test public void concat_05() { test("concat('a'@en, 'b'@fr)", "'ab'"); }
+ @Test public void concat_06() { test("concat('a'^^xsd:string, 'b')", "'ab'"); }
@Test public void concat_07() { test("concat('a'^^xsd:string, 'b'^^xsd:string)", "'ab'^^xsd:string"); }
- //@Test public void concat_08() { test("concat('a', 'b'^^xsd:string)", "'ab'^^xsd:string"); }
- @Test public void concat_08() { test("concat('a', 'b'^^xsd:string)", "'ab'"); }
- @Test public void concat_09() { test("concat('a'@en, 'b'^^xsd:string)", "'ab'"); }
- @Test public void concat_10() { test("concat('a'^^xsd:string, 'b'@en)", "'ab'"); }
- @Test public void concat_11() { test("concat()", "''"); }
-
- @Test
- public void concat_90() { assertThrows(ExprEvalException.class, ()-> test("concat(1)", "1") ); }
-
- @Test
- public void concat_91() { test("concat('a'@en, 'b'@fr)", "'ab'"); }
+ @Test public void concat_08() { test("concat('a'^^xsd:string, 'b'^^xsd:string)", "'ab'^^xsd:string"); }
+ @Test public void concat_09() { test("concat('a', 'b'^^xsd:string)", "'ab'"); }
+ @Test public void concat_10() { test("concat('a'@en, 'b'^^xsd:string)", "'ab'"); }
+ @Test public void concat_11() { test("concat('a'^^xsd:string, 'b'@en)", "'ab'"); }
+ @Test public void concat_12() { test("concat()", kwEmptyString); }
+
+ @Test public void concat_20() { test("concat('a', 'b', 'c')", "'abc'"); }
+ @Test public void concat_21() { test("concat('a'@en, 'b'@en, 'c')", "'abc'"); }
+ @Test public void concat_22() { test("concat('a', 'b'@en, 'c')", "'abc'"); }
+ @Test public void concat_23() { test("concat('a', 'b', 'c'@en)", "'abc'"); }
+
+ @Test public void concat_30() { test("concat('a'@en--ltr, 'b'@en--ltr)", "'ab'@en--ltr"); }
+ @Test public void concat_31() { test("concat('a'@en--ltr, 'b'@en--rtl)", "'ab'"); }
+ @Test public void concat_32() { test("concat('a'@en--ltr, 'b'@fr--ltr)", "'ab'"); }
+ @Test public void concat_33() { test("concat('a'@en--ltr, 'b'@fr--ltr)", "'ab'"); }
+ @Test public void concat_34() { test("concat('a'@en--ltr, 'b')", "'ab'"); }
+ @Test public void concat_35() { test("concat('a'@en--ltr, 'b'@en)", "'ab'"); }
+ @Test public void concat_36() { test("concat('a', 'b'@en--ltr)", "'ab'"); }
+ @Test public void concat_37() { test("concat('a'@en, 'b'@en--ltr)", "'ab'"); }
// SUBSTR
@Test public void substr_01() { test("substr('abc',1)", "'abc'"); }
@Test public void substr_02() { test("substr('abc',2)", "'bc'"); }
@Test public void substr_03() { test("substr('abc',2,1)", "'b'"); }
- @Test public void substr_04() { test("substr('abc',2,0)", "''"); }
+ @Test public void substr_04() { test("substr('abc',2,0)", kwEmptyString); }
@Test public void substr_05() { test("substr('12345',0,3)", "'12'"); }
@Test public void substr_06() { test("substr('12345',-1,3)", "'1'"); }
@@ -168,18 +181,15 @@ public class TestFunctions2
@Test public void substr_11() { test("substr('metadata', 4, 3)", "'ada'"); }
@Test public void substr_12() { test("substr('12345', 1.5, 2.6)", "'234'"); }
@Test public void substr_13() { test("substr('12345', 0, 3)", "'12'"); }
- @Test public void substr_14() { test("substr('12345', 5, -3)", "''"); }
+ @Test public void substr_14() { test("substr('12345', 5, -3)", kwEmptyString); }
@Test public void substr_15() { test("substr('12345', -3, 5)", "'1'"); }
- @Test public void substr_16() { test("substr('12345', 0/0E0, 3)", "''"); }
- @Test public void substr_17() { test("substr('12345', 1, 0/0E0)", "''"); }
- @Test public void substr_18() { test("substr('', 1, 3)", "''"); }
+ @Test public void substr_16() { test("substr('12345', 0/0E0, 3)", kwEmptyString); }
+ @Test public void substr_17() { test("substr('12345', 1, 0/0E0)", kwEmptyString); }
+ @Test public void substr_18() { test("substr('', 1, 3)", kwEmptyString); }
- @Test
- public void substr_20() { assertThrows(ExprEvalException.class, ()-> test("substr(1, 1, 3)", "''") ); }
- @Test
- public void substr_21() { assertThrows(ExprEvalException.class, ()-> test("substr('', 'one', 3)", "''") ); }
- @Test
- public void substr_22() { assertThrows(ExprEvalException.class, ()-> test("substr('', 1, 'three')", "''") ); }
+ @Test public void substr_20() { testEvalException("substr(1, 1, 3)"); }
+ @Test public void substr_21() { testEvalException("substr('', 'one', 3)"); }
+ @Test public void substr_22() { testEvalException("substr('', 1, 'three')"); }
// Codepoint outside UTF-16.
// These are U+0001F46A 👪 - FAMILY
@@ -192,7 +202,7 @@ public class TestFunctions2
@Test public void substr_30() { test("substr('\\uD83D\\uDC6A', 1)", "'\\uD83D\\uDC6A'"); }
// Same using Java string escapes.
@Test public void substr_30b() { test("substr('\uD83D\uDC6A', 1)", "'\uD83D\uDC6A'"); }
- @Test public void substr_31() { test("substr('\\uD83D\\uDC6A', 2)", "''"); }
+ @Test public void substr_31() { test("substr('\\uD83D\\uDC6A', 2)", kwEmptyString); }
@Test public void substr_32() { test("substr('ABC\\uD83D\\uDC6ADEF', 4, 1)", "'\\uD83D\\uDC6A'"); }
@Test public void substr_33() { test("substr('\\uD83D\\uDC6A!', -1, 3)", "'\\uD83D\\uDC6A'"); }
@@ -206,13 +216,13 @@ public class TestFunctions2
@Test public void ucase_01() { test("ucase('abc')", "'ABC'"); }
@Test public void ucase_02() { test("ucase('ABC')", "'ABC'"); }
@Test public void ucase_03() { test("ucase('Ab 123 Cd')", "'AB 123 CD'"); }
- @Test public void ucase_04() { test("ucase('')", "''"); }
+ @Test public void ucase_04() { test("ucase('')", kwEmptyString); }
// LCASE
@Test public void lcase_01() { test("lcase('abc')", "'abc'"); }
@Test public void lcase_02() { test("lcase('ABC')", "'abc'"); }
@Test public void lcase_03() { test("lcase('Ab 123 Cd')", "'ab 123 cd'"); }
- @Test public void lcase_04() { test("lcase('')", "''"); }
+ @Test public void lcase_04() { test("lcase('')", kwEmptyString); }
// ENCODE_FOR_URI
@Test public void encodeURI_01() { test("encode_for_uri('a:b cd/~')", "'a%3Ab%20cd%2F~'"); }
@@ -222,92 +232,210 @@ public class TestFunctions2
@Test public void encodeURI_05() { test("encode_for_uri('abc'@en)", "'abc'"); }
@Test
- public void encodeURI_09() { assertThrows(ExprEvalException.class, ()-> test("encode_for_uri(1234)", "'1234'") ); }
-
- /* Compatibility rules
- # pairs of simple literals,
- # pairs of xsd:string typed literals
- # pairs of plain literals with identical language tags
- # pairs of an xsd:string typed literal (arg1 or arg2) and a simple literal (arg2 or arg1)
- # pairs of a plain literal with language tag (arg1) and a simple literal (arg2)
- # pairs of a plain literal with language tag (arg1) and an xsd:string typed literal (arg2)
- */
-
- // CONTAINS
- @Test public void contains_01() { test("contains('abc', 'a')", "true"); }
- @Test public void contains_02() { test("contains('abc', 'b')", "true"); }
- @Test public void contains_03() { test("contains('ABC', 'a')", "false"); }
- @Test public void contains_04() { test("contains('abc', '')", "true"); }
- @Test public void contains_05() { test("contains('', '')", "true"); }
- @Test public void contains_06() { test("contains('', 'a')", "false"); }
- @Test public void contains_07() { test("contains('12345', '34')", "true"); }
- @Test public void contains_08() { test("contains('12345', '123456')", "false"); }
-
- @Test public void contains_10() { test("contains('abc', 'a'^^xsd:string)", "true"); }
- @Test public void contains_11() { assertThrows(ExprEvalException.class, ()-> test("contains('abc', 'a'@en)", "true") ); }
-
- @Test public void contains_12() { test("contains('abc'@en, 'a')", "true"); }
- @Test public void contains_13() { test("contains('abc'@en, 'a'^^xsd:string)", "true"); }
- @Test public void contains_14() { test("contains('abc'@en, 'a'@en)", "true"); }
- @Test public void contains_15() { assertThrows(ExprEvalException.class, ()-> test("contains('abc'@en, 'a'@fr)", "true") ); }
-
- @Test public void contains_16() { test("contains('abc'^^xsd:string, 'a')", "true"); }
-
- @Test public void contains_17() { assertThrows(ExprEvalException.class, ()-> test("contains('abc'^^xsd:string, 'a'@en)", "true") ); }
- @Test public void contains_18() { test("contains('abc'^^xsd:string, 'a'^^xsd:string)", "true"); }
-
- @Test public void contains_20() { assertThrows(ExprEvalException.class, ()-> test("contains(1816, 'a'^^xsd:string)", "true") ); }
- @Test public void contains_21() { assertThrows(ExprEvalException.class, ()-> test("contains('abc', 1066)", "true") ); }
-
- @Test public void strstarts_01() { test("strstarts('abc', 'a')", "true"); }
- @Test public void strstarts_02() { test("strstarts('abc', 'b')", "false"); }
- @Test public void strstarts_03() { test("strstarts('ABC', 'a')", "false"); }
- @Test public void strstarts_04() { test("strstarts('abc', '')", "true"); }
- @Test public void strstarts_05() { test("strstarts('', '')", "true"); }
- @Test public void strstarts_06() { test("strstarts('', 'a')", "false"); }
-
- @Test public void strstarts_10() { test("strstarts('abc', 'a'^^xsd:string)", "true"); }
- @Test public void strstarts_11() { assertThrows(ExprEvalException.class, ()-> test("strstarts('abc', 'a'@en)", "true") ); }
-
- @Test public void strstarts_12() { test("strstarts('abc'@en, 'a')", "true"); }
- @Test public void strstarts_13() { test("strstarts('abc'@en, 'a'^^xsd:string)", "true"); }
- @Test public void strstarts_14() { test("strstarts('abc'@en, 'a'@en)", "true"); }
- @Test
- public void strstarts_15() { assertThrows(ExprEvalException.class, ()-> test("strstarts('abc'@en, 'a'@fr)", "true") ); }
-
- @Test public void strstarts_16() { test("strstarts('abc'^^xsd:string, 'a')", "true"); }
+ public void encodeURI_09() { testEvalException("encode_for_uri(1234)"); }
- @Test
- public void strstarts_17() { assertThrows(ExprEvalException.class, ()-> test("strstarts('abc'^^xsd:string, 'a'@en)", "true") ); }
- @Test public void strstarts_18() { test("strstarts('abc'^^xsd:string, 'a'^^xsd:string)", "true"); }
+ /* Compatibility rules */
- @Test public void strstarts_20() { assertThrows(ExprEvalException.class, ()-> test("strstarts(1816, 'a'^^xsd:string)", "true") ); }
- @Test public void strstarts_21() { assertThrows(ExprEvalException.class, ()-> test("strstarts('abc', 1066)", "true") ); }
+ // CONTAINS
+ @Test public void contains_01() { test("contains('abc', 'a')", kwTRUE); }
+ @Test public void contains_02() { test("contains('abc', 'b')", kwTRUE); }
+ @Test public void contains_03() { test("contains('ABC', 'a')", kwFALSE); }
+ @Test public void contains_04() { test("contains('abc', '')", kwTRUE); }
+ @Test public void contains_05() { test("contains('', '')", kwTRUE); }
+ @Test public void contains_06() { test("contains('', 'a')", kwFALSE); }
+ @Test public void contains_07() { test("contains('12345', '34')", kwTRUE); }
+ @Test public void contains_08() { test("contains('12345', '123456')", kwFALSE); }
+
+ @Test public void contains_10() { test("contains('abc', 'abcd')", kwFALSE); }
+ @Test public void contains_11() { test("contains('abc'@en, 'bc')", kwTRUE); }
+ @Test public void contains_12() { test("contains('abc'^^xsd:string, 'c')", kwTRUE); }
+ @Test public void contains_13() { test("contains('abc'^^xsd:string, 'c'^^xsd:string)", kwTRUE); }
+ @Test public void contains_14() { test("contains('abc', 'z'^^xsd:string)", kwFALSE); }
+ @Test public void contains_15() { test("contains('abc'@en, 'abc'@en)", kwTRUE); }
+
+ @Test public void contains_16() { testEvalException("contains('ab'@en, 'ab'@fr)"); }
+ @Test public void contains_17() { testEvalException("contains(123, 'ab'@fr)"); }
+
+ @Test public void contains_20() { test("contains('abc', 'a'^^xsd:string)", kwTRUE); }
+ @Test public void contains_21() { testEvalException("contains('abc', 'a'@en)"); }
+
+ @Test public void contains_22() { test("contains('abc'@en, 'a')", kwTRUE); }
+ @Test public void contains_23() { test("contains('abc'@en, 'a'^^xsd:string)", kwTRUE); }
+ @Test public void contains_24() { test("contains('abc'@en, 'a'@en)", kwTRUE); }
+ @Test public void contains_25() { testEvalException("contains('abc'@en, 'a'@fr)"); }
+
+ @Test public void contains_26() { test("contains('abc'^^xsd:string, 'a')", kwTRUE); }
+
+ @Test public void contains_27() { testEvalException("contains('abc'^^xsd:string, 'a'@en)"); }
+ @Test public void contains_28() { test("contains('abc'^^xsd:string, 'a'^^xsd:string)", kwTRUE); }
+
+ @Test public void contains_30() { testEvalException("contains(1816, 'a'^^xsd:string)"); }
+ @Test public void contains_31() { testEvalException("contains('abc', 1066)"); }
+
+ @Test public void contains_40() { test("contains('abc'@en--ltr, 'a')", kwTRUE); }
+ @Test public void contains_41() { testEvalException("contains('abc'@en--ltr, 'a'@en)"); }
+ @Test public void contains_42() { testEvalException("contains('abc'@en, 'a'@en--ltr)"); }
+ @Test public void contains_43() { testEvalException("contains('abc', 'a'@en--ltr)"); }
+
+ // STRSTARTS
+ @Test public void strstarts_01() { test("strstarts('abc', 'a')", kwTRUE); }
+ @Test public void strstarts_02() { test("strstarts('abc', 'b')", kwFALSE); }
+ @Test public void strstarts_03() { test("strstarts('ABC', 'a')", kwFALSE); }
+ @Test public void strstarts_04() { test("strstarts('abc', '')", kwTRUE); }
+ @Test public void strstarts_05() { test("strstarts('', '')", kwTRUE); }
+ @Test public void strstarts_06() { test("strstarts('', 'a')", kwFALSE); }
+ @Test public void strstarts_07() { test("STRSTARTS('abc', 'abcd')", kwFALSE); }
+
+ @Test public void strstarts_10() { test("strstarts('abc', 'a'^^xsd:string)", kwTRUE); }
+ @Test public void strstarts_11() { testEvalException("strstarts('abc', 'a'@en)"); }
+ @Test public void strstarts_12() { test("strstarts('abc'@en, 'a')", kwTRUE); }
+ @Test public void strstarts_13() { test("strstarts('abc'@en, 'a'^^xsd:string)", kwTRUE); }
+ @Test public void strstarts_14() { test("strstarts('abc'@en, 'a'@en)", kwTRUE); }
+ @Test public void strstarts_15() { testEvalException("strstarts('abc'@en, 'a'@fr)"); }
+
+ @Test public void strstarts_16() { test("strstarts('abc'^^xsd:string, 'a')", kwTRUE); }
+ @Test public void strstarts_17() { testEvalException("strstarts('abc'^^xsd:string, 'a'@en)"); }
+ @Test public void strstarts_18() { test("strstarts('abc'^^xsd:string, 'a'^^xsd:string)", kwTRUE); }
+
+ @Test public void strstarts_20() { testEvalException("strstarts(1816, 'a'^^xsd:string)"); }
+ @Test public void strstarts_21() { testEvalException("strstarts('abc', 1066)"); }
+
+ @Test public void strstarts_22() { testEvalException("STRSTARTS('ab'@en, 'ab'@fr)"); }
+ @Test public void strstarts_23() { testEvalException("STRSTARTS(123, 'ab'@fr)"); }
+ @Test public void strstarts_24() { testEvalException("STRSTARTS('123'^^xsd:string, 12.3)"); }
+
+ @Test public void strstarts_30() { test("STRSTARTS('ab'@en--ltr, 'z')", kwFALSE); }
+ @Test public void strstarts_31() { test("STRSTARTS('ab'@en--ltr, 'z'@en--ltr)", kwFALSE); }
+ @Test public void strstarts_32() { testEvalException("STRSTARTS('ab'@en, 'z'@en--rtl)"); }
// STRENDS
- @Test public void strends_01() { test("strends('abc', 'c')", "true"); }
- @Test public void strends_02() { test("strends('abc', 'b')", "false"); }
- @Test public void strends_03() { test("strends('ABC', 'c')", "false"); }
- @Test public void strends_04() { test("strends('abc', '')", "true"); }
- @Test public void strends_05() { test("strends('', '')", "true"); }
- @Test public void strends_06() { test("strends('', 'a')", "false"); }
-
- @Test public void strends_10() { test("strends('abc', 'c'^^xsd:string)", "true"); }
- @Test public void strends11() { assertThrows(ExprEvalException.class, ()-> test("strends('abc', 'c'@en)", "true") ); }
-
- @Test public void strends_12() { test("strends('abc'@en, 'c')", "true"); }
- @Test public void strends_13() { test("strends('abc'@en, 'c'^^xsd:string)", "true"); }
- @Test public void strends_14() { test("strends('abc'@en, 'c'@en)", "true"); }
- @Test
- public void strends_15() { assertThrows(ExprEvalException.class, ()-> test("strends('abc'@en, 'c'@fr)", "true") ); }
-
- @Test public void strends_16() { test("strends('abc'^^xsd:string, 'bc')", "true"); }
- @Test
- public void strends_17() { assertThrows(ExprEvalException.class, ()-> test("strends('abc'^^xsd:string, 'a'@en)", "true") ); }
- @Test public void strends_18() { test("strends('abc'^^xsd:string, 'abc'^^xsd:string)", "true"); }
-
- @Test public void strends_20() { assertThrows(ExprEvalException.class, ()-> test("strends(1816, '6'^^xsd:string)", "true") ); }
- @Test public void strends_21() { assertThrows(ExprEvalException.class, ()-> test("strends('abc', 1066)", "true") ); }
+ @Test public void strends_01() { test("strends('abc', 'c')", kwTRUE); }
+ @Test public void strends_02() { test("strends('abc', 'b')", kwFALSE); }
+ @Test public void strends_03() { test("strends('ABC', 'c')", kwFALSE); }
+ @Test public void strends_04() { test("strends('abc', '')", kwTRUE); }
+ @Test public void strends_05() { test("strends('', '')", kwTRUE); }
+ @Test public void strends_06() { test("strends('', 'a')", kwFALSE); }
+
+ @Test public void strends_10() { test("STRENDS('abc', 'abcd')", kwFALSE); }
+ @Test public void strends_11() { test("STRENDS('abc'@en, 'bc')", kwTRUE); }
+ @Test public void strends_12() { test("STRENDS('abc'^^xsd:string, 'c')", kwTRUE); }
+ @Test public void strends_13() { test("STRENDS('abc'^^xsd:string, 'c'^^xsd:string)", kwTRUE); }
+ @Test public void strends_14() { test("STRENDS('abc', 'ab'^^xsd:string)", kwFALSE); }
+ @Test public void strends_15() { test("STRENDS('abc'@en, 'abc'@en)", kwTRUE); }
+
+ @Test public void strends_16() { testEvalException("STRENDS('ab'@en, 'ab'@fr)"); }
+ @Test public void strends_17() { testEvalException("STRENDS(123, 'ab'@fr)"); }
+ @Test public void strends_18() { testEvalException("STRENDS('123'^^xsd:string, 12.3)"); }
+
+ @Test public void strends_20() { test("strends('abc', 'c'^^xsd:string)", kwTRUE); }
+ @Test public void strends_21() { testEvalException("strends('abc', 'c'@en)"); }
+
+ @Test public void strends_22() { testEvalException("STRSTARTS('ab'@en, 'ab'@fr)"); }
+ @Test public void strends_23() { testEvalException("STRSTARTS(123, 'ab'@fr)"); }
+ @Test public void strends_24() { testEvalException("STRSTARTS('123'^^xsd:string, 12.3)"); }
+
+ @Test public void strends_30() { test("STRENDS('ab'@en--ltr, 'z')", kwFALSE); }
+ @Test public void strends_31() { test("STRENDS('ab'@en--ltr, 'z'@en--ltr)", kwFALSE); }
+ @Test public void strends_32() { testEvalException("STRENDS('ab'@en, 'z'@en--rtl)"); }
+
+ // STRBEFORE
+ @Test public void strbefore_01() { test("STRBEFORE('abc', 'abcd')", kwEmptyString); }
+ @Test public void strbefore_02() { test("STRBEFORE('abc'@en, 'b')", "'a'@en"); }
+ @Test public void strbefore_03() { test("STRBEFORE('abc'^^xsd:string, 'c')", "'ab'^^xsd:string"); }
+ @Test public void strbefore_04() { test("STRBEFORE('abc'^^xsd:string, ''^^xsd:string)", "''^^xsd:string"); }
+ @Test public void strbefore_05() { test("STRBEFORE('abc', 'ab'^^xsd:string)", kwEmptyString); }
+ @Test public void strbefore_06() { test("STRBEFORE('abc'@en, 'b'@en)", "'a'@en"); }
+
+ @Test public void strbefore_07() { testEvalException("STRBEFORE('ab'@en, 'ab'@fr)"); }
+ @Test public void strbefore_08() { testEvalException("STRBEFORE(123, 'ab'@fr)"); }
+ @Test public void strbefore_09() { testEvalException("STRBEFORE('123'^^xsd:string, 12.3)"); }
+ // No match case
+ @Test public void strbefore_10() { test("STRBEFORE('abc'^^xsd:string, 'z')", kwEmptyString); }
+ // Empty string case
+ @Test public void strbefore_11() { test("STRBEFORE('abc'^^xsd:string, '')", kwEmptyString); }
+ @Test public void strbefore_12() { testEvalException("STRBEFORE('abc', ''@en)"); }
+
+ @Test public void strbefore_30() { test("STRBEFORE('abc'@en--ltr, 'a')", "''@en--ltr"); }
+ @Test public void strbefore_31() { test("STRBEFORE('abc'@en--ltr, 'a'@en--ltr)", "''@en--ltr"); }
+ @Test public void strbefore_32() { testEvalException("STRBEFORE('abc'@en--ltr, 'a'@en)"); }
+
+ // STRAFTER
+ @Test public void strafter_01() { test("STRAFTER('abc', 'abcd')", kwEmptyString); }
+ @Test public void strafter_02() { test("STRAFTER('abc'@en, 'b')", "'c'@en"); }
+ @Test public void strafter_03() { test("STRAFTER('abc'^^xsd:string, 'a')", "'bc'^^xsd:string"); }
+ @Test public void strafter_04() { test("STRAFTER('abc'^^xsd:string, ''^^xsd:string)", "'abc'"); }
+ @Test public void strafter_05() { test("STRAFTER('abc', 'bc'^^xsd:string)", kwEmptyString); }
+ @Test public void strafter_06() { test("STRAFTER('abc'@en, 'b'@en)", "'c'@en"); }
+
+ @Test public void strafter_07() { testEvalException("STRAFTER('ab'@en, 'ab'@fr)"); }
+ @Test public void strafter_08() { testEvalException("STRAFTER(123, 'ab'@fr)"); }
+ @Test public void strafter_09() { testEvalException("STRAFTER('123'^^xsd:string, 12.3)"); }
+ // No match case
+ @Test public void strafter_10() { test("STRAFTER('abc'^^xsd:string, 'z')", kwEmptyString); }
+ // Empty string case
+ @Test public void strafter_11() { test("STRAFTER('abc'^^xsd:string, '')", "'abc'"); }
+ @Test public void strafter_12() { testEvalException("STRAFTER('abc', ''@en)"); }
+
+ @Test public void strafter_30() { test("STRAFTER('abc'@en--ltr, '')", "'abc'@en--ltr"); }
+ @Test public void strafter_31() { test("STRAFTER('abc'@en--ltr, 'a'@en--ltr)", "'bc'@en--ltr"); }
+ @Test public void strafter_32() { testEvalException("STRAFTER('abc'@en--ltr, 'a'@en)"); }
+ @Test public void strafter_33() { testEvalException("STRAFTER('abc'@en, 'a'@en--ltr)"); }
+
+ // STRREPLACE
+ @Test public void replace01() { test("REPLACE('abc', 'b', 'Z')", "'aZc'"); }
+ @Test public void replace02() { test("REPLACE('abc', 'b.', 'Z')", "'aZ'"); }
+ @Test public void replace03() { test("REPLACE('abcbd', 'b.', 'Z')", "'aZZ'"); }
+
+ @Test public void replace04() { test("REPLACE('abcbd'^^xsd:string, 'b.', 'Z')", "'aZZ'"); }
+ @Test public void replace05() { test("REPLACE('abcbd'@en, 'b.', 'Z')", "'aZZ'@en"); }
+ @Test public void replace06() { test("REPLACE('abcbd', 'B.', 'Z', 'i')", "'aZZ'"); }
+
+ // See JENA-740
+ // ARQ provides replacement of the potentially empty string.
+ @Test public void replace07() { test("REPLACE('abc', '.*', 'Z')", "'Z'"); }
+ @Test public void replace08() { test("REPLACE('', '.*', 'Z')", "'Z'"); }
+ @Test public void replace09() { test("REPLACE('abc', '.?', 'Z')", "'ZZZ'"); }
+
+ @Test public void replace10() { test("REPLACE('abc', 'XXX', 'Z')", "'abc'"); }
+ @Test public void replace11() { test("REPLACE('', '.', 'Z')", kwEmptyString); }
+ @Test public void replace12() { test("REPLACE('', '(a|b)?', 'Z')", "'Z'"); }
+ @Test public void replace13() { test("REPLACE('abc'@en, 'XXX', 'Z')", "'abc'@en"); }
+
+ // Bad group
+ @Test public void replace20() { testEvalException("REPLACE('abc', '.*', '$1')"); }
+ // Bad pattern; static (parse or build time) compilation.
+ @Test public void replace21() { assertThrows(ExprException.class, ()-> ExprUtils.parse("REPLACE('abc', '^(a){-9}', 'ABC')")); }
+
+ @Test public void replace30() { test("REPLACE('b'@en--ltr, '(a|b)?', 'Z')", "'Z'@en--ltr"); }
+ @Test public void replace31() { test("REPLACE('b'@en--ltr, '(a|b)?', 'Z'@en--ltr)", "'Z'@en--ltr"); }
+
+ @Test public void sameTerm1() { test("sameTerm(1,1)", kwTRUE); }
+ @Test public void sameTerm2() { test("sameTerm(1,1.0)", kwFALSE); }
+ @Test public void sameTerm3() { test("sameTerm(1,1e0)", kwFALSE); }
+ @Test public void sameTerm4() { test("sameTerm(<_:a>, <_:a>)", kwTRUE); }
+ @Test public void sameTerm5() { test("sameTerm(<x>, <x>)", kwTRUE); }
+ @Test public void sameTerm6() { test("sameTerm(<x>, <y>)", kwFALSE); }
+
+ @Test public void OneOf_01() { test("57 in (xsd:integer, '123')", kwFALSE); }
+ @Test public void OneOf_02() { test("57 in (57)", kwTRUE); }
+ @Test public void OneOf_03() { test("57 in (123, 57)", kwTRUE); }
+ @Test public void OneOf_04() { test("57 in (57, 456)", kwTRUE); }
+ @Test public void OneOf_05() { test("57 in (123, 57, 456)", kwTRUE); }
+ @Test public void OneOf_06() { test("57 in (1,2,3)", kwFALSE); }
+
+ @Test public void NotOneOf_01() { test("57 not in (xsd:integer, '123')", kwTRUE); }
+ @Test public void NotOneOf_02() { test("57 not in (57)", kwFALSE); }
+ @Test public void NotOneOf_03() { test("57 not in (123, 57)", kwFALSE); }
+ @Test public void NotOneOf_04() { test("57 not in (57, 456)", kwFALSE); }
+ @Test public void NotOneOf_05() { test("57 not in (123, 57, 456)", kwFALSE); }
+ @Test public void NotOneOf_06() { test("57 not in (1,2,3)", kwTRUE); }
+
+ @Test public void StrLang1() { test("strlang('xyz', 'en')", "'xyz'@en"); }
+ @Test public void StrLang2() { testEvalException("strlang('xyz', '')"); }
+
+ @Test public void StrDatatype1() { test("strdt('123', xsd:integer)", "123"); }
+ @Test public void StrDatatype2() { test("strdt('xyz', xsd:string)", "'xyz'"); }
+ @Test public void StrDatatype3() { testEvalException("strdt('123', 'datatype')"); }
// YEAR
@Test public void year_01() { test("year('2010-12-24T16:24:01.123'^^xsd:dateTime)", "2010"); }
@@ -315,106 +443,82 @@ public class TestFunctions2
@Test public void year_03() { test("year('2010'^^xsd:gYear)", "2010"); }
@Test public void year_04() { test("year('2010-12'^^xsd:gYearMonth)", "2010"); }
- @Test
- public void year_05() { assertThrows(ExprEvalException.class, ()-> test("year('--12'^^xsd:gMonth)", "2010") ); }
- @Test
- public void year_06() { assertThrows(ExprEvalException.class, ()-> test("year('--12-24'^^xsd:gMonthDay)", "2010") ); }
- @Test
- public void year_07() { assertThrows(ExprEvalException.class, ()-> test("year('---24'^^xsd:gDay)", "2010") ); }
+ @Test public void year_05() { testEvalException("year('--12'^^xsd:gMonth)"); }
+ @Test public void year_06() { testEvalException("year('--12-24'^^xsd:gMonthDay)"); }
+ @Test public void year_07() { testEvalException("year('---24'^^xsd:gDay)"); }
@Test public void year_11() { test("year('2010-12-24T16:24:01.123Z'^^xsd:dateTime)", "2010"); }
@Test public void year_12() { test("year('2010-12-24Z'^^xsd:date)", "2010"); }
@Test public void year_13() { test("year('2010Z'^^xsd:gYear)", "2010"); }
@Test public void year_14() { test("year('2010-12Z'^^xsd:gYearMonth)", "2010"); }
- @Test
- public void year_15() { assertThrows(ExprEvalException.class, ()-> test("year('--12Z'^^xsd:gMonth)", "2010") ); }
- @Test
- public void year_16() { assertThrows(ExprEvalException.class, ()-> test("year('--12-24Z'^^xsd:gMonthDay)", "2010") ); }
- @Test
- public void year_17() { assertThrows(ExprEvalException.class, ()-> test("year('---24Z'^^xsd:gDay)", "2010") ); }
+ @Test public void year_15() { testEvalException("year('--12Z'^^xsd:gMonth)"); }
+ @Test public void year_16() { testEvalException("year('--12-24Z'^^xsd:gMonthDay)"); }
+ @Test public void year_17() { testEvalException("year('---24Z'^^xsd:gDay)"); }
@Test public void year_21() { test("year('2010-12-24T16:24:01.123-08:00'^^xsd:dateTime)", "2010"); }
@Test public void year_22() { test("year('2010-12-24-08:00'^^xsd:date)", "2010"); }
@Test public void year_23() { test("year('2010-08:00'^^xsd:gYear)", "2010"); }
@Test public void year_24() { test("year('2010-12-08:00'^^xsd:gYearMonth)", "2010"); }
- @Test
- public void year_25() { assertThrows(ExprEvalException.class, ()-> test("year('--12-08:00'^^xsd:gMonth)", "2010") ); }
- @Test
- public void year_26() { assertThrows(ExprEvalException.class, ()-> test("year('--12-24-08:00'^^xsd:gMonthDay)", "2010") ); }
- @Test
- public void year_27() { assertThrows(ExprEvalException.class, ()-> test("year('---24-08:00'^^xsd:gDay)", "2010") ); }
+ @Test public void year_25() { testEvalException("year('--12-08:00'^^xsd:gMonth)"); }
+ @Test public void year_26() { testEvalException("year('--12-24-08:00'^^xsd:gMonthDay)"); }
+ @Test public void year_27() { testEvalException("year('---24-08:00'^^xsd:gDay)"); }
@Test public void year_dur_01() { test("year('P1Y2M3DT4H5M6S'^^xsd:duration)", "1"); }
-
// MONTH
@Test public void month_01() { test("month('2010-12-24T16:24:01.123'^^xsd:dateTime)", "12"); }
@Test public void month_02() { test("month('2010-12-24'^^xsd:date)", "12"); }
- @Test
- public void month_03() { assertThrows(ExprEvalException.class, ()-> test("month('2010'^^xsd:gYear)", "12") ); }
+ @Test public void month_03() { testEvalException("month('2010'^^xsd:gYear)"); }
@Test public void month_04() { test("month('2010-12'^^xsd:gYearMonth)", "12"); }
@Test public void month_05() { test("month('--12'^^xsd:gMonth)", "12"); }
@Test public void month_06() { test("month('--12-24'^^xsd:gMonthDay)", "12"); }
- @Test
- public void month_07() { assertThrows(ExprEvalException.class, ()-> test("month('---24'^^xsd:gDay)", "12") ); }
+ @Test public void month_07() { testEvalException("month('---24'^^xsd:gDay)"); }
@Test public void month_11() { test("month('2010-12-24T16:24:01.123Z'^^xsd:dateTime)", "12"); }
@Test public void month_12() { test("month('2010-12-24Z'^^xsd:date)", "12"); }
- @Test
- public void month_13() { assertThrows(ExprEvalException.class, ()-> test("month('2010Z'^^xsd:gYear)", "12") ); }
+ @Test public void month_13() { testEvalException("month('2010Z'^^xsd:gYear)"); }
@Test public void month_14() { test("month('2010-12Z'^^xsd:gYearMonth)", "12"); }
@Test public void month_15() { test("month('--12Z'^^xsd:gMonth)", "12"); }
@Test public void month_16() { test("month('--12-24Z'^^xsd:gMonthDay)", "12"); }
- @Test
- public void month_17() { assertThrows(ExprEvalException.class, ()-> test("month('---24Z'^^xsd:gDay)", "12") ); }
+ @Test public void month_17() { testEvalException("month('---24Z'^^xsd:gDay)"); }
@Test public void month_21() { test("month('2010-12-24T16:24:01.123-08:00'^^xsd:dateTime)", "12"); }
@Test public void month_22() { test("month('2010-12-24-08:00'^^xsd:date)", "12"); }
- @Test
- public void month_23() { assertThrows(ExprEvalException.class, ()-> test("month('2010-08:00'^^xsd:gYear)", "12") ); }
+ @Test public void month_23() { testEvalException("month('2010-08:00'^^xsd:gYear)"); }
@Test public void month_24() { test("month('2010-12-08:00'^^xsd:gYearMonth)", "12"); }
@Test public void month_25() { test("month('--12-08:00'^^xsd:gMonth)", "12"); }
- public void month_26() { test("month('--12-24-08:00'^^xsd:gMonthDay)", "12"); }
- @Test
- public void month_27() { assertThrows(ExprEvalException.class, ()-> test("month('---24-08:00'^^xsd:gDay)", "12") ); }
+ @Test public void month_26() { test("month('--12-24-08:00'^^xsd:gMonthDay)", "12"); }
+ @Test public void month_27() { testEvalException("month('---24-08:00'^^xsd:gDay)"); }
@Test public void month_dur_01() { test("month('P1Y2M3DT4H5M6S'^^xsd:duration)", "2"); }
// DAY
@Test public void day_01() { test("day('2010-12-24T16:24:01.123'^^xsd:dateTime)", "24"); }
@Test public void day_02() { test("day('2010-12-24'^^xsd:date)", "24"); }
- @Test
- public void day_03() { assertThrows(ExprEvalException.class, ()-> test("day('2010'^^xsd:gYear)", "24") ); }
- @Test
- public void day_04() { assertThrows(ExprEvalException.class, ()-> test("day('2010-12'^^xsd:gYearMonth)", "24") ); }
+ @Test public void day_03() { testEvalException("day('2010'^^xsd:gYear)"); }
+ @Test public void day_04() { testEvalException("day('2010-12'^^xsd:gYearMonth)"); }
- @Test
- public void day_05() { assertThrows(ExprEvalException.class, ()-> test("day('--12'^^xsd:gMonth)", "24") ); }
+ @Test public void day_05() { testEvalException("day('--12'^^xsd:gMonth)"); }
@Test public void day_06() { test("day('--12-24'^^xsd:gMonthDay)", "24"); }
@Test public void day_07() { test("day('---24'^^xsd:gDay)", "24"); }
@Test public void day_11() { test("day('2010-12-24T16:24:01.123Z'^^xsd:dateTime)", "24"); }
@Test public void day_12() { test("day('2010-12-24Z'^^xsd:date)", "24"); }
- @Test
- public void day_13() { assertThrows(ExprEvalException.class, ()-> test("day('2010Z'^^xsd:gYear)", "24") ); }
- @Test
- public void day_14() { assertThrows(ExprEvalException.class, ()-> test("day('2010-12Z'^^xsd:gYearMonth)", "24") ); }
- @Test
- public void day_15() { assertThrows(ExprEvalException.class, ()-> test("day('--12Z'^^xsd:gMonth)", "24") ); }
+
+ @Test public void day_13() { testEvalException("day('2010Z'^^xsd:gYear)"); }
+ @Test public void day_14() { testEvalException("day('2010-12Z'^^xsd:gYearMonth)"); }
+ @Test public void day_15() { testEvalException("day('--12Z'^^xsd:gMonth)"); }
@Test public void day_16() { test("day('--12-24Z'^^xsd:gMonthDay)", "24"); }
@Test public void day_17() { test("day('---24Z'^^xsd:gDay)", "24"); }
@Test public void day_21() { test("day('2010-12-24T16:24:01.123-08:00'^^xsd:dateTime)", "24"); }
@Test public void day_22() { test("day('2010-12-24-08:00'^^xsd:date)", "24"); }
- @Test
- public void day_23() { assertThrows(ExprEvalException.class, ()-> test("day('2010-08:00'^^xsd:gYear)", "24") ); }
- @Test
- public void day_24() { assertThrows(ExprEvalException.class, ()-> test("day('2010-12-08:00'^^xsd:gYearMonth)", "24") ); }
- @Test
- public void day_25() { assertThrows(ExprEvalException.class, ()-> test("day('--12-08:00'^^xsd:gMonth)", "24") ); }
+ @Test public void day_23() { testEvalException("day('2010-08:00'^^xsd:gYear)"); }
+ @Test public void day_24() { testEvalException("day('2010-12-08:00'^^xsd:gYearMonth)"); }
+ @Test public void day_25() { testEvalException("day('--12-08:00'^^xsd:gMonth)"); }
@Test public void day_26() { test("day('--12-24-08:00'^^xsd:gMonthDay)", "24"); }
@Test public void day_27() { test("day('---24-08:00'^^xsd:gDay)", "24"); }
@@ -423,8 +527,7 @@ public class TestFunctions2
// HOURS
@Test public void hours_01() { test("hours('2010-12-24T16:24:01.123'^^xsd:dateTime)", "16"); }
- @Test
- public void hours_02() { assertThrows(ExprEvalException.class, ()-> test("hours('2010-12-24'^^xsd:date)", "16") ); }
+ @Test public void hours_02() { testEvalException("hours('2010-12-24'^^xsd:date)"); }
@Test public void hours_03() { test("hours('16:24:01'^^xsd:time)", "16"); }
@Test public void hours_10() { test("hours('2010-12-24T16:24:01.123Z'^^xsd:dateTime)", "16"); }
@@ -437,7 +540,7 @@ public class TestFunctions2
// MINUTES
@Test public void minutes_01() { test("minutes('2010-12-24T16:24:01.123'^^xsd:dateTime)", "24"); }
- @Test public void minutes_02() { assertThrows(ExprEvalException.class, ()-> test("minutes('2010-12-24'^^xsd:date)", "") ); }
+ @Test public void minutes_02() { testEvalException("minutes('2010-12-24'^^xsd:date)"); }
@Test public void minutes_03() { test("minutes('16:24:01'^^xsd:time)", "24"); }
@Test public void minutes_10() { test("minutes('2010-12-24T16:24:01.123Z'^^xsd:dateTime)", "24"); }
@@ -450,8 +553,7 @@ public class TestFunctions2
// SECONDS
@Test public void seconds_01() { test("seconds('2010-12-24T16:24:01.123'^^xsd:dateTime)", "01.123"); }
- @Test
- public void seconds_02() { assertThrows(ExprEvalException.class, ()-> test("seconds('2010-12-24'^^xsd:date)", "") ); }
+ @Test public void seconds_02() { testEvalException("seconds('2010-12-24'^^xsd:date)"); }
@Test public void seconds_03() { test("seconds('16:24:01'^^xsd:time)", "'01'^^xsd:decimal"); }
@Test public void seconds_10() { test("seconds('2010-12-24T16:24:31.123Z'^^xsd:dateTime)", "31.123"); }
@@ -469,9 +571,9 @@ public class TestFunctions2
@Test public void timezone_04() { test("timezone('2010-12-24T16:24:35.123-00:00'^^xsd:dateTime)", "'-PT0S'^^xsd:dayTimeDuration"); }
@Test public void timezone_05() { test("timezone('2010-12-24T16:24:35.123+00:00'^^xsd:dateTime)", "'PT0S'^^xsd:dayTimeDuration"); }
- @Test public void timezone_09() { assertThrows(ExprEvalException.class, ()-> test("timezone('2010-12-24T16:24:35'^^xsd:dateTime)", "'PT0S'^^xsd:dayTimeDuration") ); }
- @Test public void timezone_10() { assertThrows(ExprEvalException.class, ()-> test("timezone(2010)", "'PT0S'^^xsd:dayTimeDuration") ); }
- @Test public void timezone_11() { assertThrows(ExprEvalException.class, ()-> test("timezone('2010-junk'^^xsd:gYear)", "'PT0S'^^xsd:dayTimeDuration") ); }
+ @Test public void timezone_09() { testEvalException("timezone('2010-12-24T16:24:35'^^xsd:dateTime)"); }
+ @Test public void timezone_10() { testEvalException("timezone(2010)"); }
+ @Test public void timezone_11() { testEvalException("timezone('2010-junk'^^xsd:gYear)"); }
// General "adjust-to-timezone"
@Test public void adjust_1() {
@@ -480,9 +582,7 @@ public class TestFunctions2
@Test
public void adjust_2() {
- assertThrows(ExprEvalException.class, ()->
- test("adjust('2022-12-21T05:05:07', 'PT08H'^^xsd:duration)", "'2022-12-21T05:05:07+08:00'^^xsd:dateTime")
- );
+ test("adjust('2022-12-21T05:05:07'^^xsd:dateTime, 'PT08H'^^xsd:duration)", "'2022-12-21T05:05:07+08:00'^^xsd:dateTime");
}
// General "adjust-to-timezone"
@@ -494,63 +594,62 @@ public class TestFunctions2
test("adjust(xsd:dateTime('2022-12-21T05:05:07'))", "'2022-12-21T05:05:07Z'^^xsd:dateTime");
}
-
// TZ
@Test public void tz_01() { test("tz('2010-12-24T16:24:35.123Z'^^xsd:dateTime)", "'Z'"); }
@Test public void tz_02() { test("tz('2010-12-24T16:24:35.123-08:00'^^xsd:dateTime)", "'-08:00'"); }
@Test public void tz_03() { test("tz('2010-12-24T16:24:35.123+01:00'^^xsd:dateTime)", "'+01:00'"); }
@Test public void tz_04() { test("tz('2010-12-24T16:24:35.123-00:00'^^xsd:dateTime)", "'-00:00'"); }
@Test public void tz_05() { test("tz('2010-12-24T16:24:35.123+00:00'^^xsd:dateTime)", "'+00:00'"); }
- @Test public void tz_06() { test("tz('2010-12-24T16:24:35.123'^^xsd:dateTime)", "''"); }
+ @Test public void tz_06() { test("tz('2010-12-24T16:24:35.123'^^xsd:dateTime)", kwEmptyString); }
- @Test public void tz_10() { assertThrows(ExprEvalException.class, ()-> test("tz(2010)", "''") ); }
- @Test public void tz_11() { assertThrows(ExprEvalException.class, ()-> test("tz('2010-junk'^^xsd:gYear)", "''") ); }
+ @Test public void tz_10() { testEvalException("tz(2010)"); }
+ @Test public void tz_11() { testEvalException("tz('2010-junk'^^xsd:gYear)"); }
// NOW
- //@Test public void now_01() { test("now() > '2010-12-24T16:24:35.123-08:00'^^xsd:dateTime", "true"); }
+ //@Test public void now_01() { test("now() > '2010-12-24T16:24:35.123-08:00'^^xsd:dateTime", kwTRUE); }
// MD5
@Test public void md5_01() { test("md5('abcd')","'e2fc714c4727ee9395f324cd2e7f331f'"); }
@Test public void md5_02() { test("md5('abcd'^^xsd:string)","'e2fc714c4727ee9395f324cd2e7f331f'"); }
- @Test public void md5_03() { assertThrows(ExprEvalException.class, ()-> test("md5('abcd'@en)","'e2fc714c4727ee9395f324cd2e7f331f'") ); }
- @Test public void md5_04() { assertThrows(ExprEvalException.class, ()-> test("md5(1234)","'e2fc714c4727ee9395f324cd2e7f331f'") ); }
+ @Test public void md5_03() { testEvalException("md5('abcd'@en)"); }
+ @Test public void md5_04() { testEvalException("md5(1234)"); }
// SHA1
@Test public void sha1_01() { test("sha1('abcd')","'81fe8bfe87576c3ecb22426f8e57847382917acf'"); }
@Test public void sha1_02() { test("sha1('abcd'^^xsd:string)","'81fe8bfe87576c3ecb22426f8e57847382917acf'"); }
- @Test public void sha1_03() { assertThrows(ExprEvalException.class, ()-> test("sha1('abcd'@en)","'81fe8bfe87576c3ecb22426f8e57847382917acf'") ); }
- @Test public void sha1_04() { assertThrows(ExprEvalException.class, ()-> test("sha1(123)","'81fe8bfe87576c3ecb22426f8e57847382917acf'") ); }
+ @Test public void sha1_03() { testEvalException("sha1('abcd'@en)"); }
+ @Test public void sha1_04() { testEvalException("sha1(123)"); }
// // SHA224
// @Test public void sha224_01() { test("sha224('abcd')","'e2fc714c4727ee9395f324cd2e7f331f'"); }
//
// @Test
-// public void sha224_02() { assertThrows(ExprEvalException.class, ()-> test("sha224('abcd'^^xsd:string)","'e2fc714c4727ee9395f324cd2e7f331f'") ); }
+// public void sha224_02() { testEvalException("sha224('abcd'^^xsd:string)"); }
//
// @Test
-// public void sha224_03() { assertThrows(ExprEvalException.class, ()-> test("sha224('abcd'@en)","'e2fc714c4727ee9395f324cd2e7f331f'") ); }
+// public void sha224_03() { testEvalException("sha224('abcd'@en)"); }
//
// @Test
-// public void sha224_04() { assertThrows(ExprEvalException.class, ()-> test("sha224(1234)","'e2fc714c4727ee9395f324cd2e7f331f'") ); }
+// public void sha224_04() { testEvalException("sha224(1234)"); }
// SHA256
@Test public void sha256_01() { test("sha256('abcd')","'88d4266fd4e6338d13b845fcf289579d209c897823b9217da3e161936f031589'"); }
@Test public void sha256_02() { test("sha256('abcd'^^xsd:string)","'88d4266fd4e6338d13b845fcf289579d209c897823b9217da3e161936f031589'"); }
- @Test public void sha256_03() { assertThrows(ExprEvalException.class, ()-> test("sha256('abcd'@en)","'88d4266fd4e6338d13b845fcf289579d209c897823b9217da3e161936f031589'") ); }
- @Test public void sha256_04() { assertThrows(ExprEvalException.class, ()-> test("sha256(<uri>)","'88d4266fd4e6338d13b845fcf289579d209c897823b9217da3e161936f031589'") ); }
+ @Test public void sha256_03() { testEvalException("sha256('abcd'@en)"); }
+ @Test public void sha256_04() { testEvalException("sha256(<uri>)"); }
// SHA384
@Test public void sha384_01() { test("sha384('abcd')","'1165b3406ff0b52a3d24721f785462ca2276c9f454a116c2b2ba20171a7905ea5a026682eb659c4d5f115c363aa3c79b'"); }
@Test public void sha384_02() { test("sha384('abcd'^^xsd:string)","'1165b3406ff0b52a3d24721f785462ca2276c9f454a116c2b2ba20171a7905ea5a026682eb659c4d5f115c363aa3c79b'"); }
- @Test public void sha384_03() { assertThrows(ExprEvalException.class, ()-> test("sha384('abcd'@en)","'1165b3406ff0b52a3d24721f785462ca2276c9f454a116c2b2ba20171a7905ea5a026682eb659c4d5f115c363aa3c79b'") ); }
- @Test public void sha384_04() { assertThrows(ExprEvalException.class, ()-> test("sha384(123.45)","'1165b3406ff0b52a3d24721f785462ca2276c9f454a116c2b2ba20171a7905ea5a026682eb659c4d5f115c363aa3c79b'") ); }
+ @Test public void sha384_03() { testEvalException("sha384('abcd'@en)"); }
+ @Test public void sha384_04() { testEvalException("sha384(123.45)"); }
// SHA512
@Test public void sha512_01() { test("sha512('abcd')","'d8022f2060ad6efd297ab73dcc5355c9b214054b0d1776a136a669d26a7d3b14f73aa0d0ebff19ee333368f0164b6419a96da49e3e481753e7e96b716bdccb6f'"); }
@Test public void sha512_02() { test("sha512('abcd'^^xsd:string)","'d8022f2060ad6efd297ab73dcc5355c9b214054b0d1776a136a669d26a7d3b14f73aa0d0ebff19ee333368f0164b6419a96da49e3e481753e7e96b716bdccb6f'"); }
- @Test public void sha512_03() { assertThrows(ExprEvalException.class, ()-> test("md5('abcd'@en)","'d8022f2060ad6efd297ab73dcc5355c9b214054b0d1776a136a669d26a7d3b14f73aa0d0ebff19ee333368f0164b6419a96da49e3e481753e7e96b716bdccb6f'") ); }
- @Test public void sha512_04() { assertThrows(ExprEvalException.class, ()-> test("md5(0.0e0)","'d8022f2060ad6efd297ab73dcc5355c9b214054b0d1776a136a669d26a7d3b14f73aa0d0ebff19ee333368f0164b6419a96da49e3e481753e7e96b716bdccb6f'") ); }
+ @Test public void sha512_03() { testEvalException("md5('abcd'@en)"); }
+ @Test public void sha512_04() { testEvalException("md5(0.0e0)"); }
// --------
@@ -576,4 +675,13 @@ public class TestFunctions2
// test result must be lexical form exact.
assertEquals(r, nv.asNode());
}
+
+ private void testEvalException(String exprStr) {
+ Expr expr = ExprUtils.parse(exprStr);
+ try {
+ NodeValue r = expr.eval(null, LibTestExpr.createTest());
+ fail("No exception raised");
+ }
+ catch (ExprEvalException ex) {}
+ }
}
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestStringArgCompatibility.java b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestStringArgCompatibility.java
new file mode 100644
index 0000000000..5623d80317
--- /dev/null
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestStringArgCompatibility.java
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.apache.jena.sparql.expr;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import org.junit.jupiter.api.Test;
+
+import org.apache.jena.graph.Node;
+import org.apache.jena.sparql.expr.nodevalue.NodeValueOps;
+import org.apache.jena.sparql.sse.SSE;
+
+public class TestStringArgCompatibility {
+
+ @Test public void arg_01() { testArgGood("'abc'", "'abc'"); }
+ @Test public void arg_02() { testArgGood("'abc'@en", "'abc'@en"); }
+ @Test public void arg_03() { testArgGood("'abc'@en--ltr", "'abc'@en--ltr"); }
+
+ @Test public void arg_04() { testArgBad("123"); }
+ @Test public void arg_05() { testArgBad("<123>"); }
+
+ @Test public void args2_01() { test2ArgsGood("'abc'", "''"); }
+ @Test public void args2_02() { test2ArgsGood("'abc'", "'ab'"); }
+ @Test public void args2_03() { test2ArgsGood("'abc'@en", "'ab'"); }
+ @Test public void args2_04() { test2ArgsGood("'abc'@en--ltr", "'ab'"); }
+ @Test public void args2_05() { test2ArgsGood("'abc'@en", "'ab'@en"); }
+ @Test public void args2_06() { test2ArgsGood("'abc'@en--ltr", "'ab'@en--ltr"); }
+
+ @Test public void args2_20() { test2ArgsBad("123", "123"); } // Not strings.
+ @Test public void args2_21() { test2ArgsBad("'abc'@en", "'a'@fr"); }
+ @Test public void args2_22() { test2ArgsBad("'abc'@en--ltr", "'a'@en"); }
+ @Test public void args2_23() { test2ArgsBad("'abc'@en", "'a'@en--ltr"); }
+
+ private void testArgGood(String arg, String expected) {
+ Node n = SSE.parseNode(arg);
+ NodeValue nv = NodeValue.makeNode(n);
+ Node r = NodeValueOps.checkAndGetStringLiteral("test", nv);
+ Node nExpected = SSE.parseNode(expected);
+ assertEquals(nExpected, r);
+ }
+
+ private void testArgBad(String arg) {
+ Node n = SSE.parseNode(arg);
+ NodeValue nv = NodeValue.makeNode(n);
+ assertThrows(ExprEvalException.class, ()->NodeValueOps.checkAndGetStringLiteral("test", nv));
+ }
+
+ private void test2ArgsGood(String arg1, String arg2) {
+ Node n1 = SSE.parseNode(arg1);
+ Node n2 = SSE.parseNode(arg2);
+ NodeValue nv1 = NodeValue.makeNode(n1);
+ NodeValue nv2 = NodeValue.makeNode(n2);
+ NodeValueOps.checkTwoArgumentStringLiterals("test", nv1, nv2);
+ }
+
+ private void test2ArgsBad(String arg1, String arg2) {
+ Node n1 = SSE.parseNode(arg1);
+ Node n2 = SSE.parseNode(arg2);
+ NodeValue nv1 = NodeValue.makeNode(n1);
+ NodeValue nv2 = NodeValue.makeNode(n2);
+ assertThrows(ExprEvalException.class, ()->NodeValueOps.checkTwoArgumentStringLiterals("test", nv1, nv2));
+ }
+
+}
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/lib/Lib.java b/jena-base/src/main/java/org/apache/jena/atlas/lib/Lib.java
index e31eee664d..504362cb42 100644
--- a/jena-base/src/main/java/org/apache/jena/atlas/lib/Lib.java
+++ b/jena-base/src/main/java/org/apache/jena/atlas/lib/Lib.java
@@ -65,13 +65,19 @@ public class Lib
/**
* Return true if obj1 and obj are both null or are .equals, else return false
- * Prefer {@link Objects#equals(Object, Object)}
+ * @deprecated Use {@link #equalsOrNulls(Object, Object)} {@link Objects#equals(Object, Object)}
*/
+ @Deprecated(forRemoval = true)
public static final <T> boolean equals(T obj1, T obj2) {
// Include because other equals* operations are here.
return Objects.equals(obj1, obj2);
}
+ public static final <T> boolean equalsOrNulls(T obj1, T obj2) {
+ // Include because static import works.
+ return Objects.equals(obj1, obj2);
+ }
+
/** Return true if obj1 and obj are both null or are .equals, else return false */
public static final boolean equalsIgnoreCase(String str1, String str2) {
if ( str1 == null )
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionLib.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionLib.java
index 099a6afea9..e8c3feeae0 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionLib.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionLib.java
@@ -19,6 +19,7 @@
package org.apache.jena.fuseki.servlets;
import static java.lang.String.format;
+import static org.apache.jena.atlas.lib.Lib.equalsOrNulls;
import static org.apache.jena.riot.web.HttpNames.METHOD_POST;
import java.io.ByteArrayOutputStream;
@@ -35,7 +36,6 @@ import jakarta.servlet.http.HttpServletResponse;
import org.apache.jena.atlas.RuntimeIOException;
import org.apache.jena.atlas.io.IO;
-import org.apache.jena.atlas.lib.Lib;
import org.apache.jena.atlas.web.AcceptList;
import org.apache.jena.atlas.web.ContentType;
import org.apache.jena.atlas.web.MediaType;
@@ -193,7 +193,7 @@ public class ActionLib {
public static boolean splitContains(String[] elts, String str) {
for ( int i = 0 ; i < elts.length ; i++ ) {
- if ( Lib.equals(elts[i], str) )
+ if ( equalsOrNulls(elts[i], str) )
return true;
}
return false;