diff --git a/gson/src/main/java/com/google/gson/JsonPrimitive.java b/gson/src/main/java/com/google/gson/JsonPrimitive.java index fedf64d2..f76dce04 100644 --- a/gson/src/main/java/com/google/gson/JsonPrimitive.java +++ b/gson/src/main/java/com/google/gson/JsonPrimitive.java @@ -344,7 +344,19 @@ public final class JsonPrimitive extends JsonElement { @Override public int hashCode() { - return (value == null) ? 31 : value.hashCode(); + if (value == null) { + return 31; + } + // Using recommended hashing algorithm from Effective Java for longs and doubles + if (isIntegral(this)) { + long value = getAsNumber().longValue(); + return (int) (value ^ (value >>> 32)); + } + if (isFloatingPoint(this)) { + long value = Double.doubleToLongBits(getAsNumber().doubleValue()); + return (int) (value ^ (value >>> 32)); + } + return value.hashCode(); } @Override @@ -359,6 +371,36 @@ public final class JsonPrimitive extends JsonElement { if (value == null) { return other.value == null; } + if (isIntegral(this) && isIntegral(other)) { + return getAsNumber().longValue() == other.getAsNumber().longValue(); + } + if (isFloatingPoint(this) && isFloatingPoint(other)) { + return getAsNumber().doubleValue() == other.getAsNumber().doubleValue(); + } return value.equals(other.value); } + + /** + * Returns true if the specified number is an integral type + * (Long, Integer, Short, Byte, BigInteger) + */ + private static boolean isIntegral(JsonPrimitive primitive) { + if (primitive.value instanceof Number) { + Number number = (Number) primitive.value; + return number instanceof BigInteger || number instanceof Long || number instanceof Integer + || number instanceof Short || number instanceof Byte; + } + return false; + } + + /** + * Returns true if the specified number is a floating point type (BigDecimal, double, float) + */ + private static boolean isFloatingPoint(JsonPrimitive primitive) { + if (primitive.value instanceof Number) { + Number number = (Number) primitive.value; + return number instanceof BigDecimal || number instanceof Double || number instanceof Float; + } + return false; + } } diff --git a/gson/src/test/java/com/google/gson/JsonPrimitiveTest.java b/gson/src/test/java/com/google/gson/JsonPrimitiveTest.java index 7561daa6..15c6b90d 100644 --- a/gson/src/test/java/com/google/gson/JsonPrimitiveTest.java +++ b/gson/src/test/java/com/google/gson/JsonPrimitiveTest.java @@ -81,6 +81,97 @@ public class JsonPrimitiveTest extends TestCase { } catch (NumberFormatException expected) { } } + public void testByteEqualsShort() { + JsonPrimitive p1 = new JsonPrimitive(new Byte((byte)10)); + JsonPrimitive p2 = new JsonPrimitive(new Short((short)10)); + assertEquals(p1, p2); + assertEquals(p1.hashCode(), p2.hashCode()); + } + + public void testByteEqualsInteger() { + JsonPrimitive p1 = new JsonPrimitive(new Byte((byte)10)); + JsonPrimitive p2 = new JsonPrimitive(new Integer(10)); + assertEquals(p1, p2); + assertEquals(p1.hashCode(), p2.hashCode()); + } + + public void testByteEqualsLong() { + JsonPrimitive p1 = new JsonPrimitive(new Byte((byte)10)); + JsonPrimitive p2 = new JsonPrimitive(new Long(10L)); + assertEquals(p1, p2); + assertEquals(p1.hashCode(), p2.hashCode()); + } + + public void testByteEqualsBigInteger() { + JsonPrimitive p1 = new JsonPrimitive(new Byte((byte)10)); + JsonPrimitive p2 = new JsonPrimitive(new BigInteger("10")); + assertEquals(p1, p2); + assertEquals(p1.hashCode(), p2.hashCode()); + } + + public void testShortEqualsInteger() { + JsonPrimitive p1 = new JsonPrimitive(new Short((short)10)); + JsonPrimitive p2 = new JsonPrimitive(new Integer(10)); + assertEquals(p1, p2); + assertEquals(p1.hashCode(), p2.hashCode()); + } + + public void testShortEqualsLong() { + JsonPrimitive p1 = new JsonPrimitive(new Short((short)10)); + JsonPrimitive p2 = new JsonPrimitive(new Long(10)); + assertEquals(p1, p2); + assertEquals(p1.hashCode(), p2.hashCode()); + } + + public void testShortEqualsBigInteger() { + JsonPrimitive p1 = new JsonPrimitive(new Short((short)10)); + JsonPrimitive p2 = new JsonPrimitive(new BigInteger("10")); + assertEquals(p1, p2); + assertEquals(p1.hashCode(), p2.hashCode()); + } + + public void testIntegerEqualsLong() { + JsonPrimitive p1 = new JsonPrimitive(new Integer(10)); + JsonPrimitive p2 = new JsonPrimitive(new Long(10L)); + assertEquals(p1, p2); + assertEquals(p1.hashCode(), p2.hashCode()); + } + + public void testIntegerEqualsBigInteger() { + JsonPrimitive p1 = new JsonPrimitive(new Integer(10)); + JsonPrimitive p2 = new JsonPrimitive(new BigInteger("10")); + assertEquals(p1, p2); + assertEquals(p1.hashCode(), p2.hashCode()); + } + + public void testLongEqualsBigInteger() { + JsonPrimitive p1 = new JsonPrimitive(new Long(10L)); + JsonPrimitive p2 = new JsonPrimitive(new BigInteger("10")); + assertEquals(p1, p2); + assertEquals(p1.hashCode(), p2.hashCode()); + } + + public void testFloatEqualsDouble() { + JsonPrimitive p1 = new JsonPrimitive(new Float(10.25F)); + JsonPrimitive p2 = new JsonPrimitive(new Double(10.25D)); + assertEquals(p1, p2); + assertEquals(p1.hashCode(), p2.hashCode()); + } + + public void testFloatEqualsBigDecimal() { + JsonPrimitive p1 = new JsonPrimitive(new Float(10.25F)); + JsonPrimitive p2 = new JsonPrimitive(new BigDecimal("10.25")); + assertEquals(p1, p2); + assertEquals(p1.hashCode(), p2.hashCode()); + } + + public void testDoubleEqualsBigDecimal() { + JsonPrimitive p1 = new JsonPrimitive(new Double(10.25D)); + JsonPrimitive p2 = new JsonPrimitive(new BigDecimal("10.25")); + assertEquals(p1, p2); + assertEquals(p1.hashCode(), p2.hashCode()); + } + public void testValidJsonOnToString() throws Exception { JsonPrimitive json = new JsonPrimitive("Some\nEscaped\nValue"); assertEquals("\"Some\\nEscaped\\nValue\"", json.toString());