This is an automated email from the ASF dual-hosted git repository.
andy pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/jena.git
The following commit(s) were added to refs/heads/master by this push:
new 8c09eb0 JENA-1659: fn:implicit-timezone(), afn:timezone, afn:nowtz().
new c5fc4e6 Merge pull request #523 from afs/timezone
8c09eb0 is described below
commit 8c09eb00062f896c47e524dde154db882aa356bc
Author: Andy Seaborne <[email protected]>
AuthorDate: Mon Jan 21 11:08:45 2019 +0000
JENA-1659: fn:implicit-timezone(), afn:timezone, afn:nowtz().
Move test support function out of main/FunctionEndBase into
test/LibTestExpr.
Clearing up while visiting.
---
.../java/org/apache/jena/sparql/SystemARQ.java | 6 +-
.../org/apache/jena/sparql/expr/NodeValue.java | 10 +--
.../jena/sparql/expr/nodevalue/NodeFunctions.java | 39 ++++++++-
.../jena/sparql/expr/nodevalue/XSDFuncOp.java | 45 +++++-----
.../org/apache/jena/sparql/function/CastXSD.java | 1 -
.../jena/sparql/function/FunctionEnvBase.java | 6 --
.../jena/sparql/function/StandardFunctions.java | 6 +-
.../jena/sparql/function/library/FN_Timezone.java | 35 ++++++++
.../jena/sparql/function/library/SystemVar.java | 61 ++++++--------
.../apache/jena/sparql/function/library/nowtz.java | 95 ++++++++++++++++++++++
.../jena/sparql/function/library/timezone.java | 22 +++++
.../org/apache/jena/sparql/expr/LibTestExpr.java | 11 +++
.../apache/jena/sparql/expr/TestExpressions2.java | 6 +-
.../apache/jena/sparql/expr/TestExpressions3.java | 5 +-
.../org/apache/jena/sparql/expr/TestFunctions.java | 62 ++++++++------
.../function/user/TestFunctionExpansion.java | 8 +-
.../function/user/TestFunctionNonExpansion.java | 8 +-
17 files changed, 303 insertions(+), 123 deletions(-)
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/SystemARQ.java
b/jena-arq/src/main/java/org/apache/jena/sparql/SystemARQ.java
index 27a96aa..a77a64b 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/SystemARQ.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/SystemARQ.java
@@ -42,10 +42,10 @@ public class SystemARQ
// Various system wide settings, "constants" that might change e.g. test
setups
// ** This can be loaded before the rest of ARQ is initialized **
- // NodeValues work without the context so somethings only have global
settings.
+ // NodeValues work without the context so some things only have global
settings.
- /** Control whether additon datatypes, over and above strict, minimal
SPARQL compliance, are handled.
- * Examples incldue xsd;date and simple literal/xsd:string.
+ /** Control whether addition datatypes, over and above strict, minimal
SPARQL compliance, are handled.
+ * Examples include xsd:date and simple literal/xsd:string.
*/
public static boolean ValueExtensions = true ;
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/expr/NodeValue.java
b/jena-arq/src/main/java/org/apache/jena/sparql/expr/NodeValue.java
index d1c2317..e6c54c1 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/expr/NodeValue.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/expr/NodeValue.java
@@ -115,9 +115,6 @@ public abstract class NodeValue extends ExprNode
public static boolean VerboseWarnings = true ;
public static boolean VerboseExceptions = false ;
- public static final BigInteger IntegerZERO = BigInteger.ZERO ;
- public static final BigDecimal DecimalZERO = BigDecimal.ZERO ;
-
public static final NodeValue TRUE = NodeValue.makeNode("true",
XSDboolean) ;
public static final NodeValue FALSE = NodeValue.makeNode("false",
XSDboolean) ;
@@ -1041,28 +1038,23 @@ public abstract class NodeValue extends ExprNode
}
if ( (datatype.equals(XSDdateTime) ||
datatype.equals(XSDdateTimeStamp)) && XSDdateTime.isValid(lex) ) {
- XSDDateTime dateTime = (XSDDateTime)lit.getValue() ;
return new NodeValueDT(lex, node) ;
}
if ( datatype.equals(XSDdate) && XSDdate.isValidLiteral(lit) ) {
// Jena datatype support works on masked dataTimes.
- XSDDateTime dateTime = (XSDDateTime)lit.getValue() ;
+ //XSDDateTime dateTime = (XSDDateTime)lit.getValue() ;
return new NodeValueDT(lex, node) ;
}
if ( datatype.equals(XSDtime) && XSDtime.isValidLiteral(lit) ) {
- // Jena datatype support works on masked dataTimes.
- XSDDateTime time = (XSDDateTime)lit.getValue() ;
return new NodeValueDT(lex, node) ;
}
if ( datatype.equals(XSDgYear) && XSDgYear.isValidLiteral(lit) ) {
- XSDDateTime time = (XSDDateTime)lit.getValue() ;
return new NodeValueDT(lex, node) ;
}
if ( datatype.equals(XSDgYearMonth) &&
XSDgYearMonth.isValidLiteral(lit) ) {
- XSDDateTime time = (XSDDateTime)lit.getValue() ;
return new NodeValueDT(lex, node) ;
}
if ( datatype.equals(XSDgMonth) && XSDgMonth.isValidLiteral(lit) )
{
diff --git
a/jena-arq/src/main/java/org/apache/jena/sparql/expr/nodevalue/NodeFunctions.java
b/jena-arq/src/main/java/org/apache/jena/sparql/expr/nodevalue/NodeFunctions.java
index a7cb286..23b7c65 100644
---
a/jena-arq/src/main/java/org/apache/jena/sparql/expr/nodevalue/NodeFunctions.java
+++
b/jena-arq/src/main/java/org/apache/jena/sparql/expr/nodevalue/NodeFunctions.java
@@ -18,9 +18,15 @@
package org.apache.jena.sparql.expr.nodevalue ;
+import java.math.BigDecimal;
+import java.math.BigInteger;
import java.util.Iterator ;
import java.util.UUID ;
+import javax.xml.datatype.DatatypeConstants;
+import javax.xml.datatype.Duration;
+import javax.xml.datatype.DatatypeConstants.Field;
+
import org.apache.jena.atlas.logging.Log ;
import org.apache.jena.datatypes.xsd.XSDDatatype ;
import org.apache.jena.graph.Node ;
@@ -496,5 +502,36 @@ public class NodeFunctions {
throw new ExprEvalException("Empty lang tag") ;
return NodeValue.makeLangString(lex, lang) ;
}
-
+
+ /** A duration, tided */
+ public static Duration duration(int seconds) {
+ if ( seconds == 0 )
+ return XSDFuncOp.zeroDuration;
+ Duration dur = NodeValue.xmlDatatypeFactory.newDuration(1000*seconds);
+ // Neaten the duration. Not all the fields ar zero.
+ dur = NodeValue.xmlDatatypeFactory.newDuration(dur.getSign()>=0,
+ field(dur,
DatatypeConstants.YEARS),
+ field(dur,
DatatypeConstants.MONTHS),
+ field(dur,
DatatypeConstants.DAYS),
+ field(dur,
DatatypeConstants.HOURS),
+ field(dur,
DatatypeConstants.MINUTES),
+ field2(dur,
DatatypeConstants.SECONDS));
+ return dur;
+ }
+
+ //Don't set field if zero.
+ private static BigInteger field(Duration dur, Field field) {
+ BigInteger i = (BigInteger)dur.getField(field);
+ if ( i == null || i.equals(BigInteger.ZERO) )
+ return null;
+ return i;
+ }
+
+ private static BigDecimal field2(Duration dur, Field field) {
+ BigDecimal x = (BigDecimal)dur.getField(field);
+ //x = x.setScale(0);
+ if ( x.compareTo(BigDecimal.ZERO) == 0 )
+ return null;
+ return x;
+ }
}
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 80dfa7b..91feb0e 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
@@ -37,6 +37,7 @@ import java.text.DecimalFormat ;
import java.text.DecimalFormatSymbols ;
import java.text.Normalizer;
import java.text.NumberFormat ;
+import java.time.ZonedDateTime;
import java.util.*;
import java.util.regex.Matcher ;
import java.util.regex.Pattern ;
@@ -230,9 +231,9 @@ public class XSDFuncOp
// Plain literals.
return ! nv.getString().isEmpty() ;
if ( nv.isInteger() )
- return !nv.getInteger().equals(NodeValue.IntegerZERO) ;
+ return !nv.getInteger().equals(BigInteger.ZERO) ;
if ( nv.isDecimal() )
- return !nv.getDecimal().equals(NodeValue.DecimalZERO) ;
+ return !nv.getDecimal().equals(BigDecimal.ZERO) ;
if ( nv.isDouble() )
return nv.getDouble() != 0.0 ;
NodeValue.raise(new ExprEvalException("Not a boolean effective value
(wrong type): " + nv)) ;
@@ -1542,7 +1543,7 @@ public class XSDFuncOp
}
private static NodeValue accessDuration(NodeValue nv, Field field) {
- Duration dur = valueCanonicalDuration(nv) ;
+ Duration dur = nv.getDuration();
// if ( ! nv.isDuration() )
// throw new ExprEvalException("Not a duration: "+nv) ;
Number x = dur.getField(field) ;
@@ -1558,34 +1559,28 @@ public class XSDFuncOp
return NodeValue.makeInteger((BigInteger)x) ;
}
- private static Duration zeroDuration =
NodeValue.xmlDatatypeFactory.newDuration(0) ;
- private static Duration valueCanonicalDuration(NodeValue nv) {
- // Unclear.
- /* This semi-normalizes a duration value - the time part is normalized.
- * Maybe > 24 hours -> set days, but not done here.
- * Because months are variable, XSD F&O does not define
- */
- // TODO - note that the accessors return 0 for unset fields.
- Duration dur = nv.getDuration() ;
-// Number xHours = dur.getField(DatatypeConstants.HOURS) ;
-// Number xMins = dur.getField(DatatypeConstants.MINUTES) ;
-// Number xSeconds = dur.getField(DatatypeConstants.SECONDS) ;
-// boolean normalize =
-// ( xHours == null || xHours.longValue() >= 24 ) ||
-// ( xMins == null || xMins.longValue() >= 60 ) ||
-// ( xSeconds == null || xSeconds.longValue() >= 60 ) ;
-// if ( normalize )
-// dur = ...
- return dur ;
+ public static Duration zeroDuration =
NodeValue.xmlDatatypeFactory.newDuration(true, null, null, null, null, null,
BigDecimal.ZERO) ;
+
+ public static NodeValue localTimezone() {
+ Duration dur = localTimezoneDuration();
+ NodeValue nv = NodeValue.makeDuration(dur);
+ return nv;
}
-
+
+ /** Local (query engine) timezone including DST */
+ private static Duration localTimezoneDuration() {
+ ZonedDateTime zdt = ZonedDateTime.now();
+ int tzDurationInSeconds = zdt.getOffset().getTotalSeconds();
+ Duration dur = NodeFunctions.duration(tzDurationInSeconds);
+ return dur;
+ }
+
public static NodeValue adjustDatetimeToTimezone(NodeValue nv1,NodeValue
nv2){
if(nv1 == null)
return null;
- if(!nv1.isDateTime() && !nv1.isDate() && !nv1.isTime()){
+ if(!nv1.isDateTime() && !nv1.isDate() && !nv1.isTime())
throw new ExprEvalException("Not a valid date, datetime or
time:"+nv1);
- }
XMLGregorianCalendar calValue = nv1.getDateTime();
Boolean hasTz = calValue.getTimezone() !=
DatatypeConstants.FIELD_UNDEFINED;
diff --git
a/jena-arq/src/main/java/org/apache/jena/sparql/function/CastXSD.java
b/jena-arq/src/main/java/org/apache/jena/sparql/function/CastXSD.java
index 8919334..806d092 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/function/CastXSD.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/CastXSD.java
@@ -143,7 +143,6 @@ public class CastXSD extends FunctionBase1 implements
FunctionFactory {
if ( nv.isYearMonthDuration() )
return NodeValue.makeNode("PT0S", castType) ;
Duration d2 = NodeValue.xmlDatatypeFactory.newDuration
-
(d.getSign()>=0,
null, null,
(BigInteger)d.getField(DatatypeConstants.DAYS),
(BigInteger)d.getField(DatatypeConstants.HOURS),
(BigInteger)d.getField(DatatypeConstants.MINUTES),
(BigDecimal)d.getField(DatatypeConstants.SECONDS)) ;
diff --git
a/jena-arq/src/main/java/org/apache/jena/sparql/function/FunctionEnvBase.java
b/jena-arq/src/main/java/org/apache/jena/sparql/function/FunctionEnvBase.java
index c531fce..ae62a12 100644
---
a/jena-arq/src/main/java/org/apache/jena/sparql/function/FunctionEnvBase.java
+++
b/jena-arq/src/main/java/org/apache/jena/sparql/function/FunctionEnvBase.java
@@ -33,12 +33,6 @@ public class FunctionEnvBase implements FunctionEnv
private DatasetGraph dataset ;
private ExecutionContext execContext = null ;
- /** Create an execution environment suitable for testing functions and
expressions */
- public static FunctionEnv createTest()
- {
- return new FunctionEnvBase(ARQ.getContext()) ;
- }
-
public FunctionEnvBase() { this(ARQ.getContext(), null, null) ; }
public FunctionEnvBase(Context context) { this ( context, null, null) ; }
diff --git
a/jena-arq/src/main/java/org/apache/jena/sparql/function/StandardFunctions.java
b/jena-arq/src/main/java/org/apache/jena/sparql/function/StandardFunctions.java
index a4e2662..393c753 100644
---
a/jena-arq/src/main/java/org/apache/jena/sparql/function/StandardFunctions.java
+++
b/jena-arq/src/main/java/org/apache/jena/sparql/function/StandardFunctions.java
@@ -198,6 +198,11 @@ public class StandardFunctions
// 9.8.2 fn:format-date
// 9.8.3 fn:format-time
+
+// 15.6 fn:implicit-timezone -> xsd:dayTimeDuration
+ add(registry, xfn+"implicit-timezone", FN_Timezone.class) ;
+ // Also available as afn:timezone.
+
// math:
// 4.8 Trigonometric and exponential functions
// 4.8.1 math:pi
@@ -241,7 +246,6 @@ public class StandardFunctions
add(registry, xfn+"apply", FN_Apply.class);
add(registry, xfn+"collation-key", FN_CollationKey.class);
-
// And add op:'s
// 4.2.1 op:numeric-add
// 4.2.2 op:numeric-subtract
diff --git
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_Timezone.java
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_Timezone.java
new file mode 100644
index 0000000..a171aa7
--- /dev/null
+++
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_Timezone.java
@@ -0,0 +1,35 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.sparql.function.library;
+
+import org.apache.jena.sparql.expr.NodeValue ;
+import org.apache.jena.sparql.expr.nodevalue.XSDFuncOp;
+import org.apache.jena.sparql.function.FunctionBase0;
+
+/**
+ * fn:implicit-timezone
+ */
+public class FN_Timezone extends FunctionBase0
+{
+ @Override
+ public NodeValue exec() {
+ return XSDFuncOp.localTimezone();
+ }
+}
+
diff --git
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/SystemVar.java
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/SystemVar.java
index 97c7431..98af66f 100644
---
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/SystemVar.java
+++
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/SystemVar.java
@@ -18,48 +18,41 @@
package org.apache.jena.sparql.function.library;
-import org.apache.jena.atlas.lib.Lib ;
-import org.apache.jena.graph.Node ;
-import org.apache.jena.sparql.engine.binding.Binding ;
-import org.apache.jena.sparql.expr.ExprEvalException ;
-import org.apache.jena.sparql.expr.ExprException ;
-import org.apache.jena.sparql.expr.ExprList ;
-import org.apache.jena.sparql.expr.NodeValue ;
-import org.apache.jena.sparql.function.Function ;
-import org.apache.jena.sparql.function.FunctionEnv ;
-import org.apache.jena.sparql.util.Symbol ;
+import org.apache.jena.atlas.lib.Lib;
+import org.apache.jena.graph.Node;
+import org.apache.jena.sparql.expr.ExprEvalException;
+import org.apache.jena.sparql.expr.ExprException;
+import org.apache.jena.sparql.expr.NodeValue;
+import org.apache.jena.sparql.function.FunctionBase0;
+import org.apache.jena.sparql.function.FunctionEnv;
+import org.apache.jena.sparql.util.Symbol;
/**
* Function that returns the value of a system variable.
*/
-public class SystemVar implements Function
-{
- private Symbol systemSymbol ;
- protected SystemVar(Symbol systemSymbol)
- {
+public class SystemVar extends FunctionBase0 {
+ private Symbol systemSymbol;
+
+ protected SystemVar(Symbol systemSymbol) {
if ( systemSymbol == null )
- throw new ExprException("System symbol is null ptr") ;
- this.systemSymbol = systemSymbol ;
+ throw new ExprException("System symbol is null ptr");
+ this.systemSymbol = systemSymbol;
}
-
+
// Need to intercept exec so we can get to the FunctionEnv
@Override
- public NodeValue exec(Binding binding, ExprList args, String uri,
FunctionEnv env) {
- // Ignore arguments.
- Object obj = env.getContext().get(systemSymbol) ;
- if ( obj == null )
- throw new ExprEvalException("null for system symbol:
"+systemSymbol) ;
- if ( ! ( obj instanceof Node ) )
- throw new ExprEvalException("Not a Node: "+Lib.className(obj)) ;
-
- Node n = (Node)obj ;
-// if ( n == null )
-// throw new ExprEvalException("No value for system variable:
"+systemSymbol) ;
- // NodeValue.makeNode could have a cache.
- NodeValue nv = NodeValue.makeNode(n) ;
- return nv ;
+ public NodeValue exec() {
+ return get(systemSymbol, super.functionEnv);
}
- @Override
- public void build(String uri, ExprList args) {}
+ public static NodeValue get(Symbol symbol, FunctionEnv env) {
+ Object obj = env.getContext().get(symbol);
+ if ( obj == null )
+ throw new ExprEvalException("null for symbol: " + symbol);
+ if ( !(obj instanceof Node) )
+ throw new ExprEvalException("Not a Node: " + Lib.className(obj));
+ Node n = (Node)obj;
+ NodeValue nv = NodeValue.makeNode(n);
+ return nv;
+ }
}
diff --git
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/nowtz.java
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/nowtz.java
new file mode 100644
index 0000000..ba6d387
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/nowtz.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.sparql.function.library;
+
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+
+import org.apache.jena.atlas.lib.DateTimeUtils;
+import org.apache.jena.graph.Node;
+
+//import org.apache.commons.logging.*;
+
+import org.apache.jena.sparql.ARQConstants ;
+import org.apache.jena.sparql.SystemARQ;
+import org.apache.jena.sparql.expr.NodeValue;
+import org.apache.jena.sparql.expr.nodevalue.XSDFuncOp;
+import org.apache.jena.sparql.function.FunctionBase0;
+import org.apache.jena.sparql.util.Context;
+import org.apache.jena.sparql.util.Symbol;
+
+/** Function that accesses the current time and returns in the timezone of the
query engine. */
+
+public class nowtz extends FunctionBase0
+{
+ public nowtz() { }
+
+ public static Symbol symNowTz = SystemARQ.allocSymbol("nowtz");
+
+ @Override
+ public NodeValue exec() {
+ Context cxt = functionEnv.getContext();
+ if ( cxt.isDefined(symNowTz) ) {
+ NodeValue nvx = cxt.get(symNowTz);
+ return nvx;
+ }
+ NodeValue nvx = execAdjust();
+// String formattedDate = fromQueryTime(cxt);
+// NodeValue nvx = NodeValue.makeNode(formattedDate, null,
XSD.dateTime.getURI());
+ cxt.set(symNowTz, nvx);
+ return nvx;
+ }
+
+ private NodeValue execAdjust() {
+ // NOW is UTC in Jena to make the same whoever is looking.
+ // For presentation reasons, you may want it in the (server) local
timezone.
+ // Calculate:
+ // fn:adjust-dateTime-to-timezone(NOW(), fn:implicit-timezone())
+ // fn:adjust-dateTime-to-timezone(NOW(), afn:timezone())
+
+ // Query time, in UTC.
+ NodeValue nv = SystemVar.get(ARQConstants.sysCurrentTime,
super.functionEnv);
+ // Timezone as xsd:dayTimeDuration.
+ NodeValue nvTz = XSDFuncOp.localTimezone();
+ // Comes out as "Z", not "+00:00" because of cal.toXMLFormat() in
NodeValue.makeDateTime
+ return XSDFuncOp.adjustDatetimeToTimezone(nv, nvTz);
+ }
+
+ // For information. Do it by accessing the query current time and
converting using
+ // ZonedDateTime.withZoneSameInstant (from Java8 java.time).
+
+ private static DateTimeFormatter dtf =
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSxxxxx");
+ private static ZoneId zoneIdUTC = ZoneOffset.UTC;
+
+ private static String fromQueryTime(Context cxt) {
+ // In UTC.
+ Node n = cxt.get(ARQConstants.sysCurrentTime);
+ String x = (n == null) ? DateTimeUtils.nowAsXSDDateTimeString() :
n.getLiteralLexicalForm();
+ ZonedDateTime zdt = dtf.parse(x, ZonedDateTime::from);
+ ZonedDateTime zdtLocal;
+ // Convert to local timezone. (maybe should put the time into context
as an Instant?)
+ if ( ! zoneIdUTC.equals(ZoneId.systemDefault()) )
+ zdtLocal = zdt.withZoneSameInstant(ZoneId.systemDefault());
+ else
+ zdtLocal = zdt;
+ return dtf.format(zdtLocal);
+ }
+}
diff --git
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/timezone.java
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/timezone.java
new file mode 100644
index 0000000..1e6408e
--- /dev/null
+++
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/timezone.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.sparql.function.library;
+
+/** Alternative name: afn:timezone for fn:implicit-timezone */
+public class timezone extends FN_Timezone {}
diff --git
a/jena-arq/src/test/java/org/apache/jena/sparql/expr/LibTestExpr.java
b/jena-arq/src/test/java/org/apache/jena/sparql/expr/LibTestExpr.java
index 20dec1c..254b74e 100644
--- a/jena-arq/src/test/java/org/apache/jena/sparql/expr/LibTestExpr.java
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/expr/LibTestExpr.java
@@ -19,11 +19,14 @@
package org.apache.jena.sparql.expr;
import static org.junit.Assert.* ;
import org.apache.jena.graph.Node ;
+import org.apache.jena.query.ARQ;
import org.apache.jena.shared.PrefixMapping ;
import org.apache.jena.shared.impl.PrefixMappingImpl ;
import org.apache.jena.sparql.ARQConstants ;
+import org.apache.jena.sparql.function.FunctionEnv;
import org.apache.jena.sparql.function.FunctionEnvBase ;
import org.apache.jena.sparql.function.library.leviathan.LeviathanConstants ;
+import org.apache.jena.sparql.util.Context;
import org.apache.jena.sparql.util.ExprUtils ;
import org.apache.jena.sparql.util.NodeFactoryExtra ;
@@ -45,6 +48,14 @@ public class LibTestExpr {
static Expr parse(String exprString) {
return ExprUtils.parse(exprString, pmap);
}
+
+ /** Create an execution environment suitable for testing functions and
expressions */
+ public static FunctionEnv createTest()
+ {
+ Context cxt = ARQ.getContext().copy();
+ cxt.set(ARQConstants.sysCurrentTime, NodeFactoryExtra.nowAsDateTime())
;
+ return new FunctionEnvBase(cxt) ;
+ }
static NodeValue eval(String exprString) {
Expr expr = ExprUtils.parse(exprString, pmap);
diff --git
a/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestExpressions2.java
b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestExpressions2.java
index 9f08dd6..62193b8 100644
--- a/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestExpressions2.java
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestExpressions2.java
@@ -20,11 +20,7 @@ package org.apache.jena.sparql.expr;
import org.apache.jena.atlas.junit.BaseTest ;
import org.apache.jena.query.QueryParseException ;
-import org.apache.jena.sparql.expr.Expr ;
-import org.apache.jena.sparql.expr.ExprEvalException ;
-import org.apache.jena.sparql.expr.NodeValue ;
import org.apache.jena.sparql.expr.nodevalue.XSDFuncOp ;
-import org.apache.jena.sparql.function.FunctionEnvBase ;
import org.apache.jena.sparql.util.ExprUtils ;
import org.junit.Test ;
@@ -152,7 +148,7 @@ public class TestExpressions2 extends BaseTest
/*package*/ static void eval(String string, boolean result)
{
Expr expr = ExprUtils.parse(string) ;
- NodeValue nv = expr.eval(null, FunctionEnvBase.createTest()) ;
+ NodeValue nv = expr.eval(null, LibTestExpr.createTest()) ;
boolean b = XSDFuncOp.booleanEffectiveValue(nv) ;
assertEquals(string, result, b) ;
}
diff --git
a/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestExpressions3.java
b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestExpressions3.java
index 4a33e6e..464f372 100644
--- a/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestExpressions3.java
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestExpressions3.java
@@ -21,7 +21,6 @@ package org.apache.jena.sparql.expr;
import org.apache.jena.atlas.junit.BaseTest ;
import org.apache.jena.sparql.engine.binding.Binding ;
import org.apache.jena.sparql.expr.nodevalue.XSDFuncOp ;
-import org.apache.jena.sparql.function.FunctionEnvBase ;
import org.apache.jena.sparql.sse.Item ;
import org.apache.jena.sparql.sse.SSE ;
import org.apache.jena.sparql.sse.builders.BuilderBinding ;
@@ -50,7 +49,7 @@ public class TestExpressions3 extends BaseTest
private static void eval(String string, String bindingStr, boolean
expected) {
Binding binding = binding(bindingStr) ;
Expr expr = ExprUtils.parse(string) ;
- NodeValue nv = expr.eval(binding, FunctionEnvBase.createTest()) ;
+ NodeValue nv = expr.eval(binding, LibTestExpr.createTest()) ;
boolean b = XSDFuncOp.booleanEffectiveValue(nv) ;
assertEquals(string, expected, b) ;
}
@@ -59,7 +58,7 @@ public class TestExpressions3 extends BaseTest
private static void evalExpr(String exprString, String bindingStr, boolean
expected) {
Binding binding = binding(bindingStr) ;
Expr expr = SSE.parseExpr(exprString) ;
- NodeValue nv = expr.eval(binding, FunctionEnvBase.createTest()) ;
+ NodeValue nv = expr.eval(binding, LibTestExpr.createTest()) ;
boolean b = XSDFuncOp.booleanEffectiveValue(nv) ;
assertEquals(exprString, expected, b) ;
}
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 e8ef732..d20f070 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
@@ -27,12 +27,12 @@ import java.text.ParseException ;
import java.text.SimpleDateFormat ;
import java.util.Date ;
import java.util.TimeZone ;
+import java.util.function.Predicate;
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.function.FunctionEnvBase ;
import org.apache.jena.sparql.util.ExprUtils ;
import org.junit.Assert ;
import org.junit.Test ;
@@ -104,7 +104,7 @@ public class TestFunctions
private static void test_exprSprintf_tz_exact(String nodeStr) {
String exprStr = "afn:sprintf('%1$tm %1$te,%1$tY',
"+NodeValue.makeDateTime(nodeStr).toString()+")" ;
Expr expr = ExprUtils.parse(exprStr) ;
- NodeValue r = expr.eval(null, FunctionEnvBase.createTest()) ;
+ NodeValue r = expr.eval(null, LibTestExpr.createTest()) ;
assertTrue(r.isString()) ;
String s = r.getString() ;
// Parse the date
@@ -126,7 +126,7 @@ public class TestFunctions
private static void test_exprSprintf_tz_possibilites(String nodeStr,
String... possible) {
String exprStr = "afn:sprintf('%1$tm %1$te,%1$tY',
"+NodeValue.makeDateTime(nodeStr).toString()+")" ;
Expr expr = ExprUtils.parse(exprStr) ;
- NodeValue r = expr.eval(null, FunctionEnvBase.createTest()) ;
+ NodeValue r = expr.eval(null, LibTestExpr.createTest()) ;
assertTrue(r.isString()) ;
String s = r.getString() ;
// Timezones! The locale data can be -1, 0, +1 from the Z day.
@@ -394,17 +394,17 @@ public class TestFunctions
"fn:adjust-dateTime-to-timezone('2002-03-07T10:00:00-07:00'^^xsd:dateTime,'"+getDynamicDurationString()+"'^^xsd:dayTimeDuration)");
}
- @Test public void
exprAdjustDatetimeToTz_03(){test("fn:adjust-dateTime-to-timezone('2002-03-07T10:00:00'^^xsd:dateTime,'-PT10H'^^xsd:dayTimeDuration)",NodeValue.makeDateTime("2002-03-07T10:00:00-10:00"));}
+ @Test public void exprAdjustDatetimeToTz_03() {
test("fn:adjust-dateTime-to-timezone('2002-03-07T10:00:00'^^xsd:dateTime,'-PT10H'^^xsd:dayTimeDuration)",NodeValue.makeDateTime("2002-03-07T10:00:00-10:00"));}
- @Test public void
exprAdjustDatetimeToTz_04(){test("fn:adjust-dateTime-to-timezone('2002-03-07T10:00:00-07:00'^^xsd:dateTime,'-PT10H'^^xsd:dayTimeDuration)",NodeValue.makeDateTime("2002-03-07T07:00:00-10:00"));}
+ @Test public void exprAdjustDatetimeToTz_04() {
test("fn:adjust-dateTime-to-timezone('2002-03-07T10:00:00-07:00'^^xsd:dateTime,'-PT10H'^^xsd:dayTimeDuration)",NodeValue.makeDateTime("2002-03-07T07:00:00-10:00"));}
- @Test public void
exprAdjustDatetimeToTz_05(){test("fn:adjust-dateTime-to-timezone('2002-03-07T10:00:00-07:00'^^xsd:dateTime,'PT10H'^^xsd:dayTimeDuration)",NodeValue.makeDateTime("2002-03-08T03:00:00+10:00"));}
+ @Test public void exprAdjustDatetimeToTz_05() {
test("fn:adjust-dateTime-to-timezone('2002-03-07T10:00:00-07:00'^^xsd:dateTime,'PT10H'^^xsd:dayTimeDuration)",NodeValue.makeDateTime("2002-03-08T03:00:00+10:00"));}
- @Test public void
exprAdjustDatetimeToTz_06(){test("fn:adjust-dateTime-to-timezone('2002-03-07T00:00:00+01:00'^^xsd:dateTime,'-PT8H'^^xsd:dayTimeDuration)",NodeValue.makeDateTime("2002-03-06T15:00:00-08:00"));}
+ @Test public void exprAdjustDatetimeToTz_06() {
test("fn:adjust-dateTime-to-timezone('2002-03-07T00:00:00+01:00'^^xsd:dateTime,'-PT8H'^^xsd:dayTimeDuration)",NodeValue.makeDateTime("2002-03-06T15:00:00-08:00"));}
- @Test public void
exprAdjustDatetimeToTz_07(){test("fn:adjust-dateTime-to-timezone('2002-03-07T10:00:00'^^xsd:dateTime,'')",NodeValue.makeDateTime("2002-03-07T10:00:00"));}
+ @Test public void exprAdjustDatetimeToTz_07() {
test("fn:adjust-dateTime-to-timezone('2002-03-07T10:00:00'^^xsd:dateTime,'')",NodeValue.makeDateTime("2002-03-07T10:00:00"));}
- @Test public void
exprAdjustDatetimeToTz_08(){test("fn:adjust-dateTime-to-timezone('2002-03-07T10:00:00-07:00'^^xsd:dateTime,'')",NodeValue.makeDateTime("2002-03-07T10:00:00"));}
+ @Test public void exprAdjustDatetimeToTz_08() {
test("fn:adjust-dateTime-to-timezone('2002-03-07T10:00:00-07:00'^^xsd:dateTime,'')",NodeValue.makeDateTime("2002-03-07T10:00:00"));}
@Test public void exprAdjustDateToTz_01(){
testEqual(
@@ -418,13 +418,13 @@ public class TestFunctions
"fn:adjust-date-to-timezone('2002-03-07-07:00'^^xsd:date,'"+getDynamicDurationString()+"'^^xsd:dayTimeDuration)");
}
- @Test public void
exprAdjustDateToTz_03(){test("fn:adjust-date-to-timezone('2002-03-07'^^xsd:date,'-PT10H'^^xsd:dayTimeDuration)",NodeValue.makeDate("2002-03-07-10:00"));}
+ @Test public void exprAdjustDateToTz_03() {
test("fn:adjust-date-to-timezone('2002-03-07'^^xsd:date,'-PT10H'^^xsd:dayTimeDuration)",NodeValue.makeDate("2002-03-07-10:00"));}
- @Test public void
exprAdjustDateToTz_04(){test("fn:adjust-date-to-timezone('2002-03-07-07:00'^^xsd:date,'-PT10H'^^xsd:dayTimeDuration)",NodeValue.makeDate("2002-03-06-10:00"));}
+ @Test public void exprAdjustDateToTz_04() {
test("fn:adjust-date-to-timezone('2002-03-07-07:00'^^xsd:date,'-PT10H'^^xsd:dayTimeDuration)",NodeValue.makeDate("2002-03-06-10:00"));}
- @Test public void
exprAdjustDateToTz_05(){test("fn:adjust-date-to-timezone('2002-03-07'^^xsd:date,'')",NodeValue.makeDate("2002-03-07"));}
+ @Test public void exprAdjustDateToTz_05() {
test("fn:adjust-date-to-timezone('2002-03-07'^^xsd:date,'')",NodeValue.makeDate("2002-03-07"));}
- @Test public void
exprAdjustDateToTz_06(){test("fn:adjust-date-to-timezone('2002-03-07-07:00'^^xsd:date,'')",NodeValue.makeDate("2002-03-07"));}
+ @Test public void exprAdjustDateToTz_06() {
test("fn:adjust-date-to-timezone('2002-03-07-07:00'^^xsd:date,'')",NodeValue.makeDate("2002-03-07"));}
@Test public void exprAdjustTimeToTz_01(){
testEqual(
@@ -438,23 +438,33 @@ public class TestFunctions
"fn:adjust-time-to-timezone('10:00:00-07:00'^^xsd:time,'"+getDynamicDurationString()+"'^^xsd:dayTimeDuration)");
}
- @Test public void
exprAdjustTimeToTz_03(){test("fn:adjust-time-to-timezone('10:00:00'^^xsd:time,'-PT10H'^^xsd:dayTimeDuration)",NodeValue.makeNode("10:00:00-10:00",XSDDatatype.XSDtime));}
+ @Test public void exprAdjustTimeToTz_03() {
test("fn:adjust-time-to-timezone('10:00:00'^^xsd:time,'-PT10H'^^xsd:dayTimeDuration)",NodeValue.makeNode("10:00:00-10:00",XSDDatatype.XSDtime));}
- @Test public void
exprAdjustTimeToTz_04(){test("fn:adjust-time-to-timezone('10:00:00-07:00'^^xsd:time,'-PT10H'^^xsd:dayTimeDuration)",NodeValue.makeNode("07:00:00-10:00",XSDDatatype.XSDtime));}
+ @Test public void exprAdjustTimeToTz_04() {
test("fn:adjust-time-to-timezone('10:00:00-07:00'^^xsd:time,'-PT10H'^^xsd:dayTimeDuration)",NodeValue.makeNode("07:00:00-10:00",XSDDatatype.XSDtime));}
- @Test public void
exprAdjustTimeToTz_05(){test("fn:adjust-time-to-timezone('10:00:00'^^xsd:time,'')",NodeValue.makeNode("10:00:00",XSDDatatype.XSDtime));}
+ @Test public void exprAdjustTimeToTz_05() {
test("fn:adjust-time-to-timezone('10:00:00'^^xsd:time,'')",NodeValue.makeNode("10:00:00",XSDDatatype.XSDtime));}
- @Test public void
exprAdjustTimeToTz_06(){test("fn:adjust-time-to-timezone('10:00:00-07:00'^^xsd:time,'')",NodeValue.makeNode("10:00:00",XSDDatatype.XSDtime));}
+ @Test public void exprAdjustTimeToTz_06() {
test("fn:adjust-time-to-timezone('10:00:00-07:00'^^xsd:time,'')",NodeValue.makeNode("10:00:00",XSDDatatype.XSDtime));}
- @Test public void
exprAdjustTimeToTz_07(){test("fn:adjust-time-to-timezone('10:00:00-07:00'^^xsd:time,'PT10H'^^xsd:dayTimeDuration)",NodeValue.makeNode("03:00:00+10:00",XSDDatatype.XSDtime));}
+ @Test public void exprAdjustTimeToTz_07() {
test("fn:adjust-time-to-timezone('10:00:00-07:00'^^xsd:time,'PT10H'^^xsd:dayTimeDuration)",NodeValue.makeNode("03:00:00+10:00",XSDDatatype.XSDtime));}
//@Test public void exprStrJoin() { test("fn:string-join('a', 'b')",
NodeValue.makeString("ab")) ; }
+ @Test public void localTimezone_1() { test("fn:implicit-timezone()",
nv->nv.isDayTimeDuration()); }
+
+ // 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() > -1 ); }
+
+ @Test public void localDateTime_3() { test("afn:nowtz() = NOW()",
NodeValue.TRUE); }
+
private static void testNumberFormat(String expression, String expected) {
Expr expr = ExprUtils.parse(expression) ;
- NodeValue r = expr.eval(null, FunctionEnvBase.createTest()) ;
+ NodeValue r = expr.eval(null, LibTestExpr.createTest()) ;
Assert.assertTrue(r.isString());
Assert.assertEquals(expected, r.getString()) ;
-
}
@Test public void formatNumber_01() {
testNumberFormat("fn:format-number(0,'#')", "0") ; }
@@ -542,20 +552,26 @@ public class TestFunctions
private void test(String exprStr, NodeValue result) {
Expr expr = ExprUtils.parse(exprStr) ;
- NodeValue r = expr.eval(null, FunctionEnvBase.createTest()) ;
+ NodeValue r = expr.eval(null, LibTestExpr.createTest()) ;
assertEquals(result, r) ;
}
+ private void test(String exprStr, Predicate<NodeValue> test) {
+ Expr expr = ExprUtils.parse(exprStr) ;
+ NodeValue r = expr.eval(null, LibTestExpr.createTest()) ;
+ assertTrue(exprStr, test.test(r));
+ }
+
private void testEqual(String exprStr, String exprStrExpected) {
Expr expr = ExprUtils.parse(exprStrExpected) ;
- NodeValue rExpected = expr.eval(null, FunctionEnvBase.createTest()) ;
+ NodeValue rExpected = expr.eval(null, LibTestExpr.createTest()) ;
test(exprStr, rExpected) ;
}
private void testEvalException(String exprStr) {
Expr expr = ExprUtils.parse(exprStr) ;
try {
- NodeValue r = expr.eval(null, FunctionEnvBase.createTest()) ;
+ 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/function/user/TestFunctionExpansion.java
b/jena-arq/src/test/java/org/apache/jena/sparql/function/user/TestFunctionExpansion.java
index 753514b..da42118 100644
---
a/jena-arq/src/test/java/org/apache/jena/sparql/function/user/TestFunctionExpansion.java
+++
b/jena-arq/src/test/java/org/apache/jena/sparql/function/user/TestFunctionExpansion.java
@@ -27,10 +27,6 @@ import org.apache.jena.sparql.expr.* ;
import org.apache.jena.sparql.expr.nodevalue.NodeValueBoolean ;
import org.apache.jena.sparql.expr.nodevalue.NodeValueDouble ;
import org.apache.jena.sparql.expr.nodevalue.NodeValueInteger ;
-import org.apache.jena.sparql.function.FunctionEnvBase ;
-import org.apache.jena.sparql.function.user.UserDefinedFunction ;
-import org.apache.jena.sparql.function.user.UserDefinedFunctionDefinition ;
-import org.apache.jena.sparql.function.user.UserDefinedFunctionFactory ;
import org.apache.jena.sparql.sse.builders.ExprBuildException ;
import org.apache.jena.sparql.util.NodeFactoryExtra ;
import org.junit.AfterClass;
@@ -333,7 +329,7 @@ public class TestFunctionExpansion {
f.build("http://example/cube", new ExprList(new NodeValueInteger(2)));
Expr actual = f.getActualExpr();
- NodeValue result = actual.eval(BindingFactory.create(),
FunctionEnvBase.createTest());
+ NodeValue result = actual.eval(BindingFactory.create(),
LibTestExpr.createTest());
Assert.assertEquals(8, NodeFactoryExtra.nodeToInt(result.asNode()));
//Change the definition of the function we depend on
@@ -344,7 +340,7 @@ public class TestFunctionExpansion {
f.build("http://example/cube", new ExprList(new NodeValueInteger(2)));
actual = f.getActualExpr();
- result = actual.eval(BindingFactory.create(),
FunctionEnvBase.createTest());
+ result = actual.eval(BindingFactory.create(),
LibTestExpr.createTest());
Assert.assertEquals(8, NodeFactoryExtra.nodeToInt(result.asNode()));
}
diff --git
a/jena-arq/src/test/java/org/apache/jena/sparql/function/user/TestFunctionNonExpansion.java
b/jena-arq/src/test/java/org/apache/jena/sparql/function/user/TestFunctionNonExpansion.java
index 4fb70bd..b5b4ef8 100644
---
a/jena-arq/src/test/java/org/apache/jena/sparql/function/user/TestFunctionNonExpansion.java
+++
b/jena-arq/src/test/java/org/apache/jena/sparql/function/user/TestFunctionNonExpansion.java
@@ -23,10 +23,6 @@ import java.util.ArrayList ;
import org.apache.jena.sparql.engine.binding.BindingFactory ;
import org.apache.jena.sparql.expr.* ;
import org.apache.jena.sparql.expr.nodevalue.NodeValueInteger ;
-import org.apache.jena.sparql.function.FunctionEnvBase ;
-import org.apache.jena.sparql.function.user.UserDefinedFunction ;
-import org.apache.jena.sparql.function.user.UserDefinedFunctionDefinition ;
-import org.apache.jena.sparql.function.user.UserDefinedFunctionFactory ;
import org.apache.jena.sparql.util.NodeFactoryExtra ;
import org.junit.AfterClass ;
import org.junit.Assert ;
@@ -85,7 +81,7 @@ public class TestFunctionNonExpansion {
f.build("http://example/cube", new ExprList(new NodeValueInteger(2)));
Expr actual = f.getActualExpr();
- NodeValue result = actual.eval(BindingFactory.create(),
FunctionEnvBase.createTest());
+ NodeValue result = actual.eval(BindingFactory.create(),
LibTestExpr.createTest());
Assert.assertEquals(8, NodeFactoryExtra.nodeToInt(result.asNode()));
//Change the definition of the function we depend on
@@ -94,7 +90,7 @@ public class TestFunctionNonExpansion {
f.build("http://example/cube", new ExprList(new NodeValueInteger(2)));
actual = f.getActualExpr();
- result = actual.eval(BindingFactory.create(),
FunctionEnvBase.createTest());
+ result = actual.eval(BindingFactory.create(),
LibTestExpr.createTest());
Assert.assertEquals(4, NodeFactoryExtra.nodeToInt(result.asNode()));
}
}