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 2019/06/07 10:50:17 UTC

[jena] branch master updated: JENA-1717: xsd:decimal canonical forms

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 15929f5  JENA-1717: xsd:decimal canonical forms
     new 5cca7dc  Merge pull request #574 from afs/xsd-decimal
15929f5 is described below

commit 15929f50cf61cabbbaa9b0de36d0684415056820
Author: Andy Seaborne <an...@apache.org>
AuthorDate: Thu May 30 18:18:14 2019 +0100

    JENA-1717: xsd:decimal canonical forms
---
 .../org/apache/jena/sparql/expr/NodeValue.java     |  2 +-
 .../sparql/expr/nodevalue/NodeValueDecimal.java    | 18 +++-----
 .../jena/sparql/expr/nodevalue/XSDFuncOp.java      | 33 +++++++++-----
 .../java/org/apache/jena/sparql/util/Utils.java    |  6 ++-
 .../apache/jena/sparql/expr/TestFunctions2.java    | 18 ++++----
 .../apache/jena/sparql/expr/TestNodeValueOps.java  |  2 +-
 .../org/apache/jena/sparql/expr/TestXSDFuncOp.java | 52 ++++++++++++++++++++++
 7 files changed, 98 insertions(+), 33 deletions(-)

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 5dc5e06..dadc33a 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
@@ -338,7 +338,7 @@ public abstract class NodeValue extends ExprNode
     
     public static NodeValue makeNodeDecimal(BigDecimal decimal)
     {
-        NodeValue nv = makeNode(Utils.stringForm(decimal), null, XSDdecimal.getURI()) ;
+        NodeValue nv = XSDFuncOp.canonicalDecimalNV(decimal) ;
         return nv ;
     }
 
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/expr/nodevalue/NodeValueDecimal.java b/jena-arq/src/main/java/org/apache/jena/sparql/expr/nodevalue/NodeValueDecimal.java
index ee704f3..9abb783 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/expr/nodevalue/NodeValueDecimal.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/expr/nodevalue/NodeValueDecimal.java
@@ -24,8 +24,6 @@ import org.apache.jena.datatypes.xsd.XSDDatatype ;
 import org.apache.jena.graph.Node ;
 import org.apache.jena.graph.NodeFactory ;
 import org.apache.jena.sparql.expr.NodeValue ;
-import org.apache.jena.sparql.util.Utils ;
-
 
 public class NodeValueDecimal extends NodeValue
 {
@@ -33,7 +31,7 @@ public class NodeValueDecimal extends NodeValue
     
     public NodeValueDecimal(BigDecimal d)         { decimal = d ; }
     public NodeValueDecimal(BigDecimal d, Node n) { super(n) ; decimal = d ; }
-    
+
     @Override
     public boolean isNumber() { return true ; }
     @Override
@@ -51,23 +49,21 @@ public class NodeValueDecimal extends NodeValue
     public double getDouble()  { return decimal.doubleValue() ; }
 
     @Override
-    protected Node makeNode()
-    { 
-        int s = decimal.scale() ;
-        return NodeFactory.createLiteral(Utils.stringForm(decimal), XSDDatatype.XSDdecimal) ;
+    protected Node makeNode() {
+        return NodeFactory.createLiteral(XSDFuncOp.canonicalDecimalStr(decimal), XSDDatatype.XSDdecimal) ;
     }
-    
+
     @Override
     public String asString() { return toString() ; }
-    
+
     @Override
     public String toString()
     { 
         // Preserve lexical form.
         if ( getNode() != null ) return super.asString() ;
-        return Utils.stringForm(decimal) ;
+        return XSDFuncOp.canonicalDecimalStr(decimal);
     }
-    
+
     @Override
     public void visit(NodeValueVisitor visitor) { visitor.visit(this) ; }
 }
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 c3ae8b4..95cc787 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
@@ -158,25 +158,29 @@ public class XSDFuncOp
     private static NodeValue decimalDivide(BigDecimal d1, BigDecimal d2) {
         try {
             BigDecimal d3 = d1.divide(d2, DIVIDE_PRECISION, BigDecimal.ROUND_FLOOR) ;
-            return messAroundWithBigDecimalFormat(d3) ;
+            return canonicalDecimalNV(d3) ;
         } catch (ArithmeticException ex) {
             Log.warn(XSDFuncOp.class, "ArithmeticException in decimal divide - attempting to treat as doubles") ;
             BigDecimal d3 = new BigDecimal(d1.doubleValue() / d2.doubleValue()) ;
             return NodeValue.makeDecimal(d3) ;
         }
     }
-    
-    private static NodeValue messAroundWithBigDecimalFormat(BigDecimal d) {
+
+    public static NodeValue canonicalDecimalNV(BigDecimal d) {
+        String x = canonicalDecimalStr(d);
+        return NodeValue.makeNode(x, XSDDatatype.XSDdecimal) ;
+    }
+
+    public static String canonicalDecimalStr(BigDecimal d) {
         String x = d.toPlainString() ;
 
         // The part after the "."
         int dotIdx = x.indexOf('.') ;
         if ( dotIdx < 0 )
             // No DOT.
-            return NodeValue.makeNode(x, XSDDatatype.XSDdecimal) ;
+            return x+".0";
 
         // Has a DOT.
-
         int i = x.length() - 1 ;
         // dotIdx+1 to leave at least ".0"
         while ((i > dotIdx + 1) && x.charAt(i) == '0')
@@ -184,12 +188,21 @@ public class XSDFuncOp
         if ( i < x.length() - 1 )
             // And trailing zeros.
             x = x.substring(0, i + 1) ;
-
-        // Avoid as expensive.
-        // x = x.replaceAll("0+$", "") ;
-        return NodeValue.makeNode(x, XSDDatatype.XSDdecimal) ;
+        // Avoid replaceAll as it is expensive.
+        // Leading zeros.
+        int j = 0;
+        for ( ; j < x.length() ; j++ ) {
+            if ( x.charAt(j) != '0')
+                break;
+        }
+        // At least one zero before dot.
+        if ( j == dotIdx )
+            j--;
+        if ( j > 0 )
+            x = x.substring(j, x.length());
+        return x;
     }
-    
+
     public static NodeValue max(NodeValue nv1, NodeValue nv2) {
         int x = compareNumeric(nv1, nv2) ;
         if ( x == Expr.CMP_LESS )
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/util/Utils.java b/jena-arq/src/main/java/org/apache/jena/sparql/util/Utils.java
index 0c1a9ea..1bc9db7 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/util/Utils.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/util/Utils.java
@@ -20,11 +20,15 @@ package org.apache.jena.sparql.util ;
 
 import java.math.BigDecimal ;
 
+import org.apache.jena.sparql.expr.nodevalue.XSDFuncOp;
+
 /** Miscellaneous operations - not query specific */
 
 public class Utils {
+    /** @deprecated Use {@link XSDFuncOp#canonicalDecimalStr}. */
+    @Deprecated
     static public String stringForm(BigDecimal decimal) {
-        return decimal.toPlainString() ;
+        return XSDFuncOp.canonicalDecimalStr(decimal);
     }
 
     static public String stringForm(double d) {
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/TestFunctions2.java
index bc5985a..16ef87d 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/TestFunctions2.java
@@ -86,9 +86,9 @@ public class TestFunctions2 extends BaseTest
     // of the implementation.
     
     @Test public void round_01()    { test("round(123)",    "123") ; }
-    @Test public void round_02()    { test("round(123.5)",  "'124'^^xsd:decimal") ; }
+    @Test public void round_02()    { test("round(123.5)",  "'124.0'^^xsd:decimal") ; }
     @Test public void round_03()    { test("round(-0.5e0)", "0.0e0") ; }
-    @Test public void round_04()    { test("round(-1.5)",   "'-1'^^xsd:decimal") ; }
+    @Test public void round_04()    { test("round(-1.5)",   "'-1.0'^^xsd:decimal") ; }
     @Test public void round_05()    { test("round(-0)",     "-0") ; }
     
     @Test public void abs_01()    { test("abs(1)",      "1") ; }
@@ -100,22 +100,22 @@ public class TestFunctions2 extends BaseTest
     
     // CEIL
     @Test public void ceil_01()    { test("ceil(1)",        "1") ; }
-    @Test public void ceil_02()    { test("ceil(1.0)",      "'1'^^xsd:decimal") ; }
+    @Test public void ceil_02()    { test("ceil(1.0)",      "'1.0'^^xsd:decimal") ; }
     @Test public void ceil_03()    { test("ceil(1e0)",      "1.0e0") ; }
     @Test public void ceil_04()    { test("ceil(1.5e0)",    "2.0e0") ; }
-    @Test public void ceil_05()    { test("ceil(-0.9)",     "'0'^^xsd:decimal") ; }
+    @Test public void ceil_05()    { test("ceil(-0.9)",     "'0.0'^^xsd:decimal") ; }
     @Test public void ceil_06()    { test("ceil(-9)",       "-9") ; }
-    @Test public void ceil_07()    { test("ceil(-9.5)",     "'-9'^^xsd:decimal") ; }
+    @Test public void ceil_07()    { test("ceil(-9.5)",     "'-9.0'^^xsd:decimal") ; }
     @Test public void ceil_08()    { test("ceil(0)",        "0") ; }
 
     // FLOOR
     @Test public void floor_01()    { test("floor(1)",      "1") ; }
-    @Test public void floor_02()    { test("floor(1.0)",    "'1'^^xsd:decimal") ; }
+    @Test public void floor_02()    { test("floor(1.0)",    "'1.0'^^xsd:decimal") ; }
     @Test public void floor_03()    { test("floor(1e0)",    "1.0e0") ; }
     @Test public void floor_04()    { test("floor(1.5e0)",  "1.0e0") ; }
-    @Test public void floor_05()    { test("floor(-0.9)",   "'-1'^^xsd:decimal") ; }
+    @Test public void floor_05()    { test("floor(-0.9)",   "'-1.0'^^xsd:decimal") ; }
     @Test public void floor_06()    { test("floor(-9)",     "-9") ; }
-    @Test public void floor_07()    { test("floor(-9.5)",   "'-10'^^xsd:decimal") ; }
+    @Test public void floor_07()    { test("floor(-9.5)",   "'-10.0'^^xsd:decimal") ; }
     @Test public void floor_08()    { test("floor(0)",      "0") ; }
 
     // simple, PLWL, xsd:string.
@@ -441,7 +441,7 @@ public class TestFunctions2 extends BaseTest
     @Test public void seconds_20()        { test("seconds('2010-12-24T16:24:35.123-08:00'^^xsd:dateTime)", "35.123") ; }
     @Test public void seconds_21()        { test("seconds('16:24:01.01-08:00'^^xsd:time)", "'01.01'^^xsd:decimal") ; }
     
-    @Test public void seconds_dur_01()    { test("seconds('P1Y2M3DT4H5M6S'^^xsd:duration)", "'6'^^xsd:decimal") ; }
+    @Test public void seconds_dur_01()    { test("seconds('P1Y2M3DT4H5M6S'^^xsd:duration)", "'6.0'^^xsd:decimal") ; }
 
     // TIMEZONE
     @Test public void timezone_01()       { test("timezone('2010-12-24T16:24:35.123Z'^^xsd:dateTime)", "'PT0S'^^xsd:dayTimeDuration") ; }
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestNodeValueOps.java b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestNodeValueOps.java
index aada461..7247c0d 100644
--- a/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestNodeValueOps.java
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestNodeValueOps.java
@@ -30,7 +30,7 @@ public class TestNodeValueOps extends BaseTest
     // ** Addition
     // Numerics
     @Test public void nv_add_1() { testAdd("12", "13", "'25'^^xsd:integer" ) ; }
-    @Test public void nv_add_2() { testAdd("'12'^^xsd:decimal", "13", "'25'^^xsd:decimal" ) ; }
+    @Test public void nv_add_2() { testAdd("'12'^^xsd:decimal", "13", "'25.0'^^xsd:decimal" ) ; }
     @Test public void nv_add_3() { testAdd("'12.0'^^xsd:decimal", "13", "'25.0'^^xsd:decimal" ) ; }
     @Test public void nv_add_4() { testAdd("12e0", "13", "25.0e0" ) ; }
     
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestXSDFuncOp.java b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestXSDFuncOp.java
index 30b853d..9619730 100644
--- a/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestXSDFuncOp.java
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestXSDFuncOp.java
@@ -18,6 +18,8 @@
 
 package org.apache.jena.sparql.expr;
 
+import java.math.BigDecimal;
+
 import org.apache.jena.atlas.junit.BaseTest ;
 import org.apache.jena.datatypes.xsd.XSDDatatype ;
 import org.apache.jena.graph.Node ;
@@ -35,6 +37,56 @@ public class TestXSDFuncOp extends BaseTest
     private static final double accuracyClose_D = 0.000001d ;
     private static final double accuracyClose_F = 0.000001f ;
     
+    
+    @Test public void lex_decimal_1() {
+        lex_decimal(BigDecimal.valueOf(0), "0.0");
+    }
+    
+    @Test public void lex_decimal_2() {
+        lex_decimal(BigDecimal.valueOf(1), "1.0");
+    }
+    
+    @Test public void lex_decimal_3() {
+        lex_decimal(BigDecimal.valueOf(0.5), "0.5");
+    }
+    
+    @Test public void lex_decimal_4() {
+        lex_decimal(BigDecimal.valueOf(-0.5), "-0.5");
+    }
+    
+    @Test public void lex_decimal_5() {
+        lex_decimal(BigDecimal.valueOf(1_000_000_000_000_000L), "1000000000000000.0");
+    }
+    
+    @Test public void lex_decimal_6() {
+        lex_decimal(BigDecimal.valueOf(-1_000_000_000_000_000L), "-1000000000000000.0");
+    }
+
+    @Test public void lex_decimal_7() {
+        lex_decimal("0.0","0.0");
+    }
+    
+    @Test public void lex_decimal_8() {
+        // As input.
+        lex_decimal("0.","0.");
+    }
+    
+    @Test public void lex_decimal_9() {
+        // As input.
+        lex_decimal("+.0","+.0");
+    }
+    
+    private static void lex_decimal(BigDecimal decimal, String expected) {
+        String lex = XSDFuncOp.canonicalDecimalStr(decimal);
+        assertEquals(expected, lex);
+    }
+    
+    private static void lex_decimal(String input, String expected) {
+        NodeValue nv = NodeValue.makeDecimal(input);
+        String lex = nv.asString(); 
+        assertEquals(expected, lex);
+    }
+
     // These add tests also test that the right kind of operation was done.
     
     @Test public void testAddIntegerInteger()