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