Fixed serialization of Longs outside of IEEE 754 range. (#123)

* Fixed serialization of Longs outside of IEEE 754 range.

Signed-off-by: Roman Grigoriadi <roman.grigoriadi@oracle.com>

* Longs outside of IEEE 754 for range negative values.

Signed-off-by: Roman Grigoriadi <roman.grigoriadi@oracle.com>

* Test for JSONP number quotation.

Signed-off-by: Roman Grigoriadi <roman.grigoriadi@oracle.com>
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/BigNumberUtil.java b/src/main/java/org/eclipse/yasson/internal/serializer/BigNumberUtil.java
index 41ccc6a..5cb0724 100644
--- a/src/main/java/org/eclipse/yasson/internal/serializer/BigNumberUtil.java
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/BigNumberUtil.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
  * which accompanies this distribution.
@@ -26,6 +26,12 @@
     // 53 means max bit value of number with sign bit included
     private static final int MAX_BIT_SIZE = 53;
 
+    // Max value for Long. Similar to JavaScript Number.MAX_SAFE_INTEGER (2**53)-1
+    private static final long MAX_JS_SAFE_VALUE = 9007199254740991L;
+
+    // Min value for Long. Similar to JavaScript Number.MIN_SAFE_INTEGER -(2**53)+1
+    private static final long MIN_JS_SAFE_VALUE = -9007199254740991L;
+
     // -1022 is the lowest range of the exponent
     // more https://en.wikipedia.org/wiki/Exponent_bias
     private static final int MIN_RANGE = -1022;
@@ -62,4 +68,16 @@
         return value.abs().bitLength() <= MAX_BIT_SIZE;
     }
 
+    /**
+     * Checks whether the value of {@link Long} matches format IEEE-754
+     *
+     * @param value value which is going to be checked
+     * @return true if value matches format IEEE-754
+     */
+    static boolean isIEEE754(Long value) {
+        return value >= MIN_JS_SAFE_VALUE && value <= MAX_JS_SAFE_VALUE;
+    }
+
+
+
 }
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/LongTypeSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/LongTypeSerializer.java
index 342841f..2d8fe91 100644
--- a/src/main/java/org/eclipse/yasson/internal/serializer/LongTypeSerializer.java
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/LongTypeSerializer.java
@@ -35,11 +35,19 @@
 
     @Override
     protected void serializeNonFormatted(Long obj, JsonGenerator generator, String key) {
-        generator.write(key, obj);
+        if (BigNumberUtil.isIEEE754(obj)) {
+            generator.write(key, obj);
+        } else {
+            generator.write(key, obj.toString());
+        }
     }
 
     @Override
     protected void serializeNonFormatted(Long obj, JsonGenerator generator) {
-        generator.write(obj);
+        if (BigNumberUtil.isIEEE754(obj)) {
+            generator.write(obj);
+        } else {
+            generator.write(obj.toString());
+        }
     }
 }
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/basic/NumberTest.java b/src/test/java/org/eclipse/yasson/defaultmapping/basic/NumberTest.java
index ed21bba..2841f3f 100644
--- a/src/test/java/org/eclipse/yasson/defaultmapping/basic/NumberTest.java
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/basic/NumberTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2016, 2017 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2018 Oracle and/or its affiliates. All rights reserved.
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
  * which accompanies this distribution.
@@ -20,8 +20,14 @@
 import org.junit.Assert;
 import org.junit.Test;
 
+import javax.json.Json;
+import javax.json.JsonObject;
+import javax.json.JsonObjectBuilder;
+import javax.json.JsonWriter;
 import javax.json.bind.Jsonb;
 import javax.json.bind.JsonbBuilder;
+import javax.json.stream.JsonGenerator;
+import java.io.StringWriter;
 import java.math.BigDecimal;
 import java.math.BigInteger;
 
@@ -120,4 +126,86 @@
         jsonString = jsonb.toJson(new Object() { public Number number = new BigDecimal("0.1000000000000001"); });
         Assert.assertEquals("{\"number\":0.1000000000000001}", jsonString);
     }
+
+    @Test
+    public void testLongIEEE748() {
+
+        // 9007199254740991L
+        Long maxJsSafeValue = Double.valueOf(Math.pow(2, 53)).longValue() - 1;
+        Long upperJsUnsafeValue = maxJsSafeValue + 1;
+
+        String json = jsonb.toJson(maxJsSafeValue);
+        Assert.assertEquals("9007199254740991", json);
+        Long deserialized = jsonb.fromJson(json, Long.class);
+        Assert.assertEquals(Long.valueOf("9007199254740991"), deserialized);
+
+        json = jsonb.toJson(upperJsUnsafeValue);
+        Assert.assertEquals("\"9007199254740992\"", json);
+        deserialized = jsonb.fromJson(json, Long.class);
+        Assert.assertEquals(Long.valueOf("9007199254740992"), deserialized);
+
+
+        Long minJsSafeValue = Math.negateExact(maxJsSafeValue);
+        Long lowerJsUnsafeValue = minJsSafeValue - 1;
+
+        json = jsonb.toJson(minJsSafeValue);
+        Assert.assertEquals("-9007199254740991", json);
+        deserialized = jsonb.fromJson(json, Long.class);
+        Assert.assertEquals(Long.valueOf("-9007199254740991"), deserialized);
+
+        json = jsonb.toJson(lowerJsUnsafeValue);
+        Assert.assertEquals("\"-9007199254740992\"", json);
+        deserialized = jsonb.fromJson(json, Long.class);
+        Assert.assertEquals(Long.valueOf("-9007199254740992"), deserialized);
+    }
+
+    /**
+     * Tests that JSON-P RI itself does no big number (out of IEEE 754 quotation).
+     * This is why it is now must be done in Yasson to match the JSONB spec.
+     */
+    @Test
+    public void testJsonpBigNumber() {
+        StringWriter w = new StringWriter();
+        JsonGenerator generator = Json.createGenerator(w);
+
+        Long maxJsSafeValue = Double.valueOf(Math.pow(2, 53)).longValue() - 1;
+        Long upperJsUnsafeValue = Long.MAX_VALUE;
+
+        generator.writeStartObject();
+        generator.write("safeLongValue", maxJsSafeValue);
+        generator.write("unsafeLongValue", upperJsUnsafeValue);
+        generator.write("safeBigDecimalValue", BigDecimal.TEN);
+        generator.write("unsafeBigDecimalValue", BigDecimal.valueOf(upperJsUnsafeValue));
+        generator.writeEnd();
+        generator.close();
+
+        Assert.assertEquals("{" +
+                        "\"safeLongValue\":9007199254740991," +
+                        "\"unsafeLongValue\":9223372036854775807," +
+                        "\"safeBigDecimalValue\":10," +
+                        "\"unsafeBigDecimalValue\":9223372036854775807}",
+                w.toString());
+
+
+        w = new StringWriter();
+        JsonWriter writer = Json.createWriter(w);
+
+
+        JsonObjectBuilder objectBuilder = Json.createObjectBuilder();
+        objectBuilder.add("safeLongValue", maxJsSafeValue);
+        objectBuilder.add("unsafeLongValue", upperJsUnsafeValue);
+        objectBuilder.add("safeBigDecimalValue", BigDecimal.valueOf(maxJsSafeValue));
+        objectBuilder.add("unsafeBigDecimalValue", BigDecimal.valueOf(upperJsUnsafeValue));
+        JsonObject build = objectBuilder.build();
+        writer.write(build);
+        writer.close();
+
+        Assert.assertEquals("{" +
+                "\"safeLongValue\":9007199254740991," +
+                "\"unsafeLongValue\":9223372036854775807," +
+                "\"safeBigDecimalValue\":9007199254740991," +
+                "\"unsafeBigDecimalValue\":9223372036854775807}", w.toString());
+
+    }
+
 }