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 2016/05/17 09:51:34 UTC
[03/14] jena git commit: Implemented fn:round (xpath3) and
fn:round-to-half-even
Implemented fn:round (xpath3) and fn:round-to-half-even
I implemented fn:round-to-half-even. While implementing it, I realized that the fn:round function was not following the xpath3 directive and I thus changed it. Still as the round function (without fn) is used in SPARQL and SPARQL 1.1 seems to be based on Xpath2 (true?), I created a new roundxpath3 function that is implementing the xpath3 specs.
Project: http://git-wip-us.apache.org/repos/asf/jena/repo
Commit: http://git-wip-us.apache.org/repos/asf/jena/commit/858de20d
Tree: http://git-wip-us.apache.org/repos/asf/jena/tree/858de20d
Diff: http://git-wip-us.apache.org/repos/asf/jena/diff/858de20d
Branch: refs/heads/master
Commit: 858de20d1e73c0c0c7ca5c3d77b7ac32bda05ba3
Parents: 6239464
Author: ales004 <ci...@hotmail.com>
Authored: Tue May 3 00:30:21 2016 +0200
Committer: ales004 <ci...@hotmail.com>
Committed: Tue May 3 00:30:21 2016 +0200
----------------------------------------------------------------------
.../jena/sparql/expr/nodevalue/XSDFuncOp.java | 61 ++++++++++++++++----
.../jena/sparql/function/StandardFunctions.java | 2 +-
.../jena/sparql/function/library/FN_Round.java | 36 ++++++++++--
.../function/library/FN_Round_Half_Even.java | 58 +++++++++++++++++++
.../apache/jena/sparql/expr/TestFunctions.java | 25 +++++++-
5 files changed, 165 insertions(+), 17 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/jena/blob/858de20d/jena-arq/src/main/java/org/apache/jena/sparql/expr/nodevalue/XSDFuncOp.java
----------------------------------------------------------------------
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 501f4ec..75ea67d 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
@@ -310,20 +310,61 @@ public class XSDFuncOp
public static NodeValue round(NodeValue v) {
switch (classifyNumeric("round", v)) {
+ case OP_INTEGER:
+ return v;
+ case OP_DECIMAL:
+ int sgn = v.getDecimal().signum();
+ BigDecimal dec;
+ if (sgn < 0)
+ dec = v.getDecimal().setScale(0, BigDecimal.ROUND_HALF_DOWN);
+ else
+ dec = v.getDecimal().setScale(0, BigDecimal.ROUND_HALF_UP);
+ return NodeValue.makeDecimal(dec);
+ case OP_FLOAT:
+ return NodeValue.makeFloat(Math.round(v.getFloat()));
+ case OP_DOUBLE:
+ return NodeValue.makeDouble(Math.round(v.getDouble()));
+ default:
+ throw new ARQInternalErrorException("Unrecognized numeric operation : " + v);
+ }
+ }
+
+ // THE FOLLOWING ROUND FUNCTION IS the xpath3 compatible version of the round function above.
+ // I created a new one because the round function is used in the E_NumRound class used for
+ // SPARQL 1.1 compatible syntax that in turn is compatible with Xpath 2 for which the spec
+ // for round was different.
+ private static BigDecimal roundDecimalValue(BigDecimal dec,int precision,boolean isHalfToEven)
+ {
+ if(isHalfToEven){
+ return dec.setScale(precision, BigDecimal.ROUND_HALF_EVEN);
+ }
+ else {
+ int sgn = dec.signum();
+ if (sgn < 0)
+ return dec.setScale(precision, BigDecimal.ROUND_HALF_DOWN);
+ else
+ return dec.setScale(precision, BigDecimal.ROUND_HALF_UP);
+ }
+ }
+
+ public static NodeValue roundXpath3(NodeValue v,NodeValue precision,boolean isHalfEven) {
+ if(!precision.isInteger()){
+ throw new ExprEvalTypeException("The precision for rounding should be an integer");
+ }
+ int precisionInt = precision.getInteger().intValue();
+ String fName = isHalfEven ? "round-half-to-even" : "round";
+ switch (classifyNumeric(fName, v)) {
case OP_INTEGER :
- return v ;
+ BigDecimal decFromInt = roundDecimalValue(new BigDecimal(v.getInteger()),precisionInt,isHalfEven);
+ return NodeValue.makeInteger(decFromInt.toBigIntegerExact());
case OP_DECIMAL :
- int sgn = v.getDecimal().signum() ;
- BigDecimal dec ;
- if ( sgn < 0 )
- dec = v.getDecimal().setScale(0, BigDecimal.ROUND_HALF_DOWN) ;
- else
- dec = v.getDecimal().setScale(0, BigDecimal.ROUND_HALF_UP) ;
- return NodeValue.makeDecimal(dec) ;
+ return NodeValue.makeDecimal(roundDecimalValue(v.getDecimal(),precisionInt,isHalfEven)) ;
case OP_FLOAT :
- return NodeValue.makeFloat(Math.round(v.getFloat())) ;
+ BigDecimal decFromFloat = roundDecimalValue(new BigDecimal(v.getFloat()),precisionInt,isHalfEven);
+ return NodeValue.makeFloat(decFromFloat.floatValue()) ;
case OP_DOUBLE :
- return NodeValue.makeDouble(Math.round(v.getDouble())) ;
+ BigDecimal decFromDouble = roundDecimalValue(new BigDecimal(v.getDouble()),precisionInt,isHalfEven);
+ return NodeValue.makeDouble(decFromDouble.doubleValue()) ;
default :
throw new ARQInternalErrorException("Unrecognized numeric operation : " + v) ;
}
http://git-wip-us.apache.org/repos/asf/jena/blob/858de20d/jena-arq/src/main/java/org/apache/jena/sparql/function/StandardFunctions.java
----------------------------------------------------------------------
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 9f9eb82..9594059 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
@@ -134,7 +134,7 @@ public class StandardFunctions
add(registry, xfn+"floor", FN_Floor.class) ;
add(registry, xfn+"round", FN_Round.class) ;
//fn:round-half-to-even
-
+ add(registry, xfn+"round-half-to-even", FN_Round_Half_Even.class) ;
// 6.1 fn:resolve-uri -- Two argument form makes sense.
// 6.2 fn:encode-for-uri
// 6.3 fn:iri-to-uri -- meaningless in SPARQL.
http://git-wip-us.apache.org/repos/asf/jena/blob/858de20d/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_Round.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_Round.java b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_Round.java
index cc7df6d..8589150 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_Round.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_Round.java
@@ -18,17 +18,43 @@
package org.apache.jena.sparql.function.library;
+import org.apache.jena.atlas.lib.Lib;
+import org.apache.jena.query.QueryBuildException;
+import org.apache.jena.sparql.expr.ExprEvalException;
+import org.apache.jena.sparql.expr.ExprList;
import org.apache.jena.sparql.expr.NodeValue ;
import org.apache.jena.sparql.expr.nodevalue.XSDFuncOp ;
-import org.apache.jena.sparql.function.FunctionBase1 ;
+import org.apache.jena.sparql.function.FunctionBase;
+
+import java.util.List;
/** round(expression) */
-public class FN_Round extends FunctionBase1
+public class FN_Round extends FunctionBase
{
public FN_Round() { super() ; }
-
+
+ @Override
+ public void checkBuild(String uri, ExprList args)
+ {
+ if ( args.size() != 1 && args.size() != 2 )
+ throw new QueryBuildException("Function '"+ Lib.className(this)+"' takes one or two arguments") ;
+ }
@Override
- public NodeValue exec(NodeValue v)
- { return XSDFuncOp.round(v) ; }
+ public NodeValue exec(List<NodeValue> args)
+ {
+ if ( args.size() > 2 )
+ throw new ExprEvalException("substring: Wrong number of arguments: "+
+ args.size()+" : [wanted 1 or 2]") ;
+
+ NodeValue v1 = args.get(0) ;
+
+ if ( args.size() == 2 )
+ {
+ NodeValue v2 = args.get(1) ;
+ return XSDFuncOp.roundXpath3(v1, v2,false) ;
+ }
+
+ return XSDFuncOp.roundXpath3(v1, NodeValue.makeInteger(0),false) ;
+ }
}
http://git-wip-us.apache.org/repos/asf/jena/blob/858de20d/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_Round_Half_Even.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_Round_Half_Even.java b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_Round_Half_Even.java
new file mode 100644
index 0000000..1b35803
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_Round_Half_Even.java
@@ -0,0 +1,58 @@
+/*
+ * 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.atlas.lib.Lib;
+import org.apache.jena.query.QueryBuildException;
+import org.apache.jena.sparql.expr.ExprEvalException;
+import org.apache.jena.sparql.expr.ExprList;
+import org.apache.jena.sparql.expr.NodeValue;
+import org.apache.jena.sparql.expr.nodevalue.XSDFuncOp;
+import org.apache.jena.sparql.function.FunctionBase;
+
+import java.util.List;
+
+public class FN_Round_Half_Even extends FunctionBase
+{
+ public FN_Round_Half_Even() { super() ; }
+
+ @Override
+ public void checkBuild(String uri, ExprList args)
+ {
+ if ( args.size() != 1 && args.size() != 2 )
+ throw new QueryBuildException("Function '"+ Lib.className(this)+"' takes one or two arguments") ;
+ }
+ @Override
+ public NodeValue exec(List<NodeValue> args)
+ {
+ if ( args.size() > 2 )
+ throw new ExprEvalException("substring: Wrong number of arguments: "+
+ args.size()+" : [wanted 1 or 2]") ;
+
+ NodeValue v1 = args.get(0) ;
+
+ if ( args.size() == 2 )
+ {
+ NodeValue v2 = args.get(1) ;
+ return XSDFuncOp.roundXpath3(v1, v2,true) ;
+ }
+
+ return XSDFuncOp.roundXpath3(v1, NodeValue.makeInteger(0),true) ;
+ }
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/858de20d/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestFunctions.java
----------------------------------------------------------------------
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 cf38e46..2161abc 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
@@ -246,7 +246,30 @@ public class TestFunctions
@Test public void exprBoolean8() { test("fn:not('X')", FALSE) ; }
@Test public void exprBoolean9() { test("fn:not(1)", FALSE) ; }
@Test public void exprBoolean10() { test("fn:not(0)", TRUE) ; }
-
+
+ @Test public void exprRound_01() { test("fn:round(123)", NodeValue.makeInteger(123)) ; }
+ @Test public void exprRound_02() { test("fn:round(123.5)", NodeValue.makeDecimal(124)) ; }
+ @Test public void exprRound_03() { test("fn:round(-0.5e0)", NodeValue.makeDouble(0.0e0)) ; }
+ @Test public void exprRound_04() { test("fn:round(-1.5)", NodeValue.makeDecimal(-1)) ; }
+ // !! I don't think that this is working correctly also if the test is passing... need to check!
+ @Test public void exprRound_05() { test("fn:round(-0)", NodeValue.makeInteger("-0")) ; }
+ @Test public void exprRound_06() { test("fn:round(1.125, 2)", NodeValue.makeDecimal(1.13)) ; }
+ @Test public void exprRound_07() { test("fn:round(8452, -2)", NodeValue.makeInteger(8500)) ; }
+ @Test public void exprRound_08() { test("fn:round(3.1415e0, 2)", NodeValue.makeDouble(3.14e0)) ; }
+ // counter-intuitive -- would fail if float/double not translated to decimal
+ @Test public void exprRound_09() { test("fn:round(35.425e0, 2)", NodeValue.makeDouble(35.42)) ; }
+
+ @Test public void exprRoundHalfEven_01() { test("fn:round-half-to-even(0.5)", NodeValue.makeDecimal(0)) ; }
+ @Test public void exprRoundHalfEven_02() { test("fn:round-half-to-even(1.5)", NodeValue.makeDecimal(2)) ; }
+ @Test public void exprRoundHalfEven_03() { test("fn:round-half-to-even(2.5)", NodeValue.makeDecimal(2)) ; }
+ @Test public void exprRoundHalfEven_04() { test("fn:round-half-to-even(3.567812e+3, 2)", NodeValue.makeDouble(3567.81e0)) ; }
+ // !! I don't think that this is working correctly also if the test is passing... need to check!
+ @Test public void exprRoundHalfEven_05() { test("fn:round-half-to-even(-0)", NodeValue.makeInteger(-0)) ; }
+ @Test public void exprRoundHalfEven_06() { test("fn:round-half-to-even(4.7564e-3, 2)", NodeValue.makeDouble(0.0e0)) ; }
+ @Test public void exprRoundHalfEven_07() { test("fn:round-half-to-even(35612.25, -2)", NodeValue.makeDecimal(35600)) ; }
+ // counter-intuitive -- would fail if float/double not translated to decimal
+ @Test public void exprRoundHalfEven_08() { test("fn:round-half-to-even(150.015, 2)", NodeValue.makeDouble(150.01)) ; }
+
//@Test public void exprStrJoin() { test("fn:string-join('a', 'b')", NodeValue.makeString("ab")) ; }
@Test public void exprSameTerm1() { test("sameTerm(1,1)", TRUE) ; }