From 86662e518c26113ebd009674b33f14669b21d16f Mon Sep 17 00:00:00 2001 From: Gabor Date: Sun, 8 Oct 2017 08:21:15 +0200 Subject: [PATCH 1/3] BigDecimal and BigInteger can be serialized --- .../google/firebase/database/core/SyncTree.java | 3 ++- .../firebase/database/snapshot/NodeUtilities.java | 6 ++++++ .../utilities/encoding/CustomClassMapper.java | 14 ++++++++++++-- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/google/firebase/database/core/SyncTree.java b/src/main/java/com/google/firebase/database/core/SyncTree.java index 17fcad445..05c71444f 100644 --- a/src/main/java/com/google/firebase/database/core/SyncTree.java +++ b/src/main/java/com/google/firebase/database/core/SyncTree.java @@ -742,7 +742,8 @@ public Node calcCompleteEventCache(Path path, List writeIdsToExclude) { if (currentSyncPoint != null) { serverCache = currentSyncPoint.getCompleteServerCache(relativePath); } - } while (!pathToFollow.isEmpty() && serverCache == null); + } + while (!pathToFollow.isEmpty() && serverCache == null); return this.pendingWriteTree.calcCompleteEventCache(path, serverCache, writeIdsToExclude, true); } diff --git a/src/main/java/com/google/firebase/database/snapshot/NodeUtilities.java b/src/main/java/com/google/firebase/database/snapshot/NodeUtilities.java index 36e1e603c..041ef123a 100644 --- a/src/main/java/com/google/firebase/database/snapshot/NodeUtilities.java +++ b/src/main/java/com/google/firebase/database/snapshot/NodeUtilities.java @@ -20,6 +20,8 @@ import com.google.firebase.database.collection.ImmutableSortedMap; import com.google.firebase.database.core.ServerValues; +import java.math.BigDecimal; +import java.math.BigInteger; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -58,6 +60,10 @@ public static Node NodeFromJSON(Object value, Node priority) throws DatabaseExce return new DoubleNode((Double) value, priority); } else if (value instanceof Boolean) { return new BooleanNode((Boolean) value, priority); + } else if (value instanceof BigDecimal) { + return new DoubleNode(((BigDecimal) value).doubleValue(), priority); + } else if (value instanceof BigInteger) { + return new LongNode(((BigInteger)value).longValue(), priority); } else if (value instanceof Map || value instanceof List) { Map childData; // TODO: refine this and use same code to iterate over array and map by building diff --git a/src/main/java/com/google/firebase/database/utilities/encoding/CustomClassMapper.java b/src/main/java/com/google/firebase/database/utilities/encoding/CustomClassMapper.java index 28542c150..28d288c9d 100644 --- a/src/main/java/com/google/firebase/database/utilities/encoding/CustomClassMapper.java +++ b/src/main/java/com/google/firebase/database/utilities/encoding/CustomClassMapper.java @@ -36,6 +36,8 @@ import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.lang.reflect.WildcardType; +import java.math.BigDecimal; +import java.math.BigInteger; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -44,6 +46,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -190,7 +193,7 @@ private static T deserializeToClass(Object obj, Class clazz) { if (obj == null) { return null; } else if (clazz.isPrimitive() - || Number.class.isAssignableFrom(clazz) + || isPrimitiveNumeric(clazz) || Boolean.class.isAssignableFrom(clazz) || Character.class.isAssignableFrom(clazz)) { return deserializeToPrimitive(obj, clazz); @@ -213,6 +216,12 @@ private static T deserializeToClass(Object obj, Class clazz) { return convertBean(obj, clazz); } } + + private static boolean isPrimitiveNumeric(Class clazz) { + return Number.class.isAssignableFrom(clazz) + && !BigDecimal.class.isAssignableFrom(clazz) + && !BigInteger.class.isAssignableFrom(clazz); + } @SuppressWarnings({"unchecked", "TypeParameterUnusedInFormals"}) private static T deserializeToParameterizedType(Object obj, ParameterizedType type) { @@ -531,7 +540,8 @@ public BeanMapper(Class clazz) { // Traverse class hierarchy until we reach java.lang.Object which contains a bunch // of fields/getters we don't want to serialize currentClass = currentClass.getSuperclass(); - } while (currentClass != null && !currentClass.equals(Object.class)); + } + while (currentClass != null && !currentClass.equals(Object.class)); if (properties.isEmpty()) { throw new DatabaseException("No properties to serialize found on class " + clazz.getName()); From 930c78183ee30bbe05f53492409c1f63366be0fc Mon Sep 17 00:00:00 2001 From: Gabor Date: Mon, 9 Oct 2017 16:10:24 +0200 Subject: [PATCH 2/3] added new node types --- .../database/snapshot/BigDecimalNode.java | 60 +++++++++++++++++++ .../database/snapshot/BigIntegerNode.java | 60 +++++++++++++++++++ .../database/snapshot/NodeUtilities.java | 12 ++-- 3 files changed, 126 insertions(+), 6 deletions(-) create mode 100644 src/main/java/com/google/firebase/database/snapshot/BigDecimalNode.java create mode 100644 src/main/java/com/google/firebase/database/snapshot/BigIntegerNode.java diff --git a/src/main/java/com/google/firebase/database/snapshot/BigDecimalNode.java b/src/main/java/com/google/firebase/database/snapshot/BigDecimalNode.java new file mode 100644 index 000000000..40c63a495 --- /dev/null +++ b/src/main/java/com/google/firebase/database/snapshot/BigDecimalNode.java @@ -0,0 +1,60 @@ +package com.google.firebase.database.snapshot; + +import java.math.BigDecimal; + +public class BigDecimalNode extends LeafNode { + + private final String value; + + public BigDecimalNode(BigDecimal value, Node priority) { + this(value == null ? null : value.toString(), priority); + } + + BigDecimalNode(String value, Node priority) { + super(priority); + this.value = value; + } + + @Override + public Node updatePriority(Node priority) { + return new BigDecimalNode(this.value, priority); + } + + @Override + public Object getValue() { + return this.value; + } + + @Override + public String getHashRepresentation(HashVersion version) { + return null; + } + + @Override + protected LeafType getLeafType() { + return LeafType.String; + } + + @Override + protected int compareLeafValues(BigDecimalNode other) { + if (other == null) { + return 1; + } + return this.value.compareTo(other.value); + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof BigDecimalNode)) { + return false; + } + BigDecimalNode otherBigDecimalNode = (BigDecimalNode) other; + return this.value.equals(otherBigDecimalNode.value) + && this.priority.equals(otherBigDecimalNode.priority); + } + + @Override + public int hashCode() { + return this.value.hashCode() + this.priority.hashCode(); + } +} diff --git a/src/main/java/com/google/firebase/database/snapshot/BigIntegerNode.java b/src/main/java/com/google/firebase/database/snapshot/BigIntegerNode.java new file mode 100644 index 000000000..8c1ea881d --- /dev/null +++ b/src/main/java/com/google/firebase/database/snapshot/BigIntegerNode.java @@ -0,0 +1,60 @@ +package com.google.firebase.database.snapshot; + +import java.math.BigInteger; + +public class BigIntegerNode extends LeafNode { + + private final String value; + + public BigIntegerNode(BigInteger value, Node priority) { + this(value == null ? null : value.toString(), priority); + } + + BigIntegerNode(String value, Node priority) { + super(priority); + this.value = value; + } + + @Override + public Node updatePriority(Node priority) { + return new BigIntegerNode(this.value, priority); + } + + @Override + public Object getValue() { + return this.value; + } + + @Override + public String getHashRepresentation(HashVersion version) { + return null; + } + + @Override + protected LeafType getLeafType() { + return LeafType.String; + } + + @Override + protected int compareLeafValues(BigIntegerNode other) { + if (other == null) { + return 1; + } + return this.value.compareTo(other.value); + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof BigIntegerNode)) { + return false; + } + BigIntegerNode otherBigDecimalNode = (BigIntegerNode) other; + return this.value.equals(otherBigDecimalNode.value) + && this.priority.equals(otherBigDecimalNode.priority); + } + + @Override + public int hashCode() { + return this.value.hashCode() + this.priority.hashCode(); + } +} diff --git a/src/main/java/com/google/firebase/database/snapshot/NodeUtilities.java b/src/main/java/com/google/firebase/database/snapshot/NodeUtilities.java index 041ef123a..f15a7f5eb 100644 --- a/src/main/java/com/google/firebase/database/snapshot/NodeUtilities.java +++ b/src/main/java/com/google/firebase/database/snapshot/NodeUtilities.java @@ -61,9 +61,9 @@ public static Node NodeFromJSON(Object value, Node priority) throws DatabaseExce } else if (value instanceof Boolean) { return new BooleanNode((Boolean) value, priority); } else if (value instanceof BigDecimal) { - return new DoubleNode(((BigDecimal) value).doubleValue(), priority); + return new BigDecimalNode(((BigDecimal) value), priority); } else if (value instanceof BigInteger) { - return new LongNode(((BigInteger)value).longValue(), priority); + return new BigIntegerNode(((BigInteger) value), priority); } else if (value instanceof Map || value instanceof List) { Map childData; // TODO: refine this and use same code to iterate over array and map by building @@ -105,8 +105,8 @@ public static Node NodeFromJSON(Object value, Node priority) throws DatabaseExce if (childData.isEmpty()) { return EmptyNode.Empty(); } else { - ImmutableSortedMap childSet = - ImmutableSortedMap.Builder.fromMap(childData, ChildrenNode.NAME_ONLY_COMPARATOR); + ImmutableSortedMap childSet = ImmutableSortedMap.Builder + .fromMap(childData, ChildrenNode.NAME_ONLY_COMPARATOR); return new ChildrenNode(childSet, priority); } } else { @@ -119,8 +119,8 @@ public static Node NodeFromJSON(Object value, Node priority) throws DatabaseExce } // CSON: MethodName - public static int nameAndPriorityCompare( - ChildKey key1, Node priority1, ChildKey key2, Node priority2) { + public static int nameAndPriorityCompare(ChildKey key1, Node priority1, ChildKey key2, + Node priority2) { int priCmp = priority1.compareTo(priority2); if (priCmp != 0) { From b621a221a0910d671c3ecee05d6bd6db958d8b12 Mon Sep 17 00:00:00 2001 From: Gabor Hajba Date: Thu, 12 Oct 2017 12:46:30 +0200 Subject: [PATCH 3/3] added some tests and code changes --- .../firebase/database/core/SyncTree.java | 3 +- .../database/snapshot/BigDecimalNode.java | 26 ++++++++--- .../database/snapshot/BigIntegerNode.java | 26 ++++++++--- .../google/firebase/database/MapperTest.java | 45 +++++++++++++++++++ .../firebase/database/snapshot/NodeTest.java | 30 ++++++++++++- 5 files changed, 117 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/google/firebase/database/core/SyncTree.java b/src/main/java/com/google/firebase/database/core/SyncTree.java index 05c71444f..17fcad445 100644 --- a/src/main/java/com/google/firebase/database/core/SyncTree.java +++ b/src/main/java/com/google/firebase/database/core/SyncTree.java @@ -742,8 +742,7 @@ public Node calcCompleteEventCache(Path path, List writeIdsToExclude) { if (currentSyncPoint != null) { serverCache = currentSyncPoint.getCompleteServerCache(relativePath); } - } - while (!pathToFollow.isEmpty() && serverCache == null); + } while (!pathToFollow.isEmpty() && serverCache == null); return this.pendingWriteTree.calcCompleteEventCache(path, serverCache, writeIdsToExclude, true); } diff --git a/src/main/java/com/google/firebase/database/snapshot/BigDecimalNode.java b/src/main/java/com/google/firebase/database/snapshot/BigDecimalNode.java index 40c63a495..ba57fc4f5 100644 --- a/src/main/java/com/google/firebase/database/snapshot/BigDecimalNode.java +++ b/src/main/java/com/google/firebase/database/snapshot/BigDecimalNode.java @@ -1,3 +1,19 @@ +/* + * Copyright 2017 Google Inc. + * + * Licensed 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 com.google.firebase.database.snapshot; import java.math.BigDecimal; @@ -22,17 +38,17 @@ public Node updatePriority(Node priority) { @Override public Object getValue() { - return this.value; + return new BigDecimal(this.value); } @Override public String getHashRepresentation(HashVersion version) { - return null; + return getPriorityHash(version) + "bigdecimal:" + this.value; } @Override protected LeafType getLeafType() { - return LeafType.String; + return LeafType.Number; } @Override @@ -49,8 +65,8 @@ public boolean equals(Object other) { return false; } BigDecimalNode otherBigDecimalNode = (BigDecimalNode) other; - return this.value.equals(otherBigDecimalNode.value) - && this.priority.equals(otherBigDecimalNode.priority); + return this.value.equals(otherBigDecimalNode.value) + && this.priority.equals(otherBigDecimalNode.priority); } @Override diff --git a/src/main/java/com/google/firebase/database/snapshot/BigIntegerNode.java b/src/main/java/com/google/firebase/database/snapshot/BigIntegerNode.java index 8c1ea881d..cf6cb0955 100644 --- a/src/main/java/com/google/firebase/database/snapshot/BigIntegerNode.java +++ b/src/main/java/com/google/firebase/database/snapshot/BigIntegerNode.java @@ -1,3 +1,19 @@ +/* + * Copyright 2017 Google Inc. + * + * Licensed 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 com.google.firebase.database.snapshot; import java.math.BigInteger; @@ -22,17 +38,17 @@ public Node updatePriority(Node priority) { @Override public Object getValue() { - return this.value; + return new BigInteger(this.value); } @Override public String getHashRepresentation(HashVersion version) { - return null; + return getPriorityHash(version) + "bigdecimal:" + this.value; } @Override protected LeafType getLeafType() { - return LeafType.String; + return LeafType.Number; } @Override @@ -49,8 +65,8 @@ public boolean equals(Object other) { return false; } BigIntegerNode otherBigDecimalNode = (BigIntegerNode) other; - return this.value.equals(otherBigDecimalNode.value) - && this.priority.equals(otherBigDecimalNode.priority); + return this.value.equals(otherBigDecimalNode.value) + && this.priority.equals(otherBigDecimalNode.priority); } @Override diff --git a/src/test/java/com/google/firebase/database/MapperTest.java b/src/test/java/com/google/firebase/database/MapperTest.java index f6a5892e0..f0097bcd5 100644 --- a/src/test/java/com/google/firebase/database/MapperTest.java +++ b/src/test/java/com/google/firebase/database/MapperTest.java @@ -22,6 +22,9 @@ import static org.junit.Assert.fail; import com.google.firebase.database.utilities.encoding.CustomClassMapper; + +import java.math.BigDecimal; +import java.math.BigInteger; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -30,6 +33,7 @@ import java.util.List; import java.util.Map; import java.util.Set; + import org.junit.Test; public class MapperTest { @@ -248,6 +252,31 @@ public void primitiveDeserializeLong() { } catch (DatabaseException e) { // ignore } } + + @Test + public void bigIntegerDeserialize() { + BigIntegerBean beanBigInteger = deserialize("{'value':'1'}", BigIntegerBean.class); + assertEquals(0,BigInteger.ONE.compareTo(beanBigInteger.getValue())); + + BigIntegerBean beanBigIntegerBig = + deserialize("{'value':'991234569874563214569874563214735'}", + BigIntegerBean.class); + assertEquals(0, new BigInteger("991234569874563214569874563214735") + .compareTo(beanBigIntegerBig.getValue())); + } + + @Test + public void bigDecimalDeserialize() { + BigDecimalBean beanBigDecimal = deserialize("{'value':'1.0'}", + BigDecimalBean.class); + assertEquals(0,BigDecimal.ONE.compareTo(beanBigDecimal.getValue())); + + BigDecimalBean beanBigDecimalBig = + deserialize("{'value':'991234569874563214569874563214735.77874'}", + BigDecimalBean.class); + assertEquals(0, new BigDecimal("991234569874563214569874563214735.77874") + .compareTo(beanBigDecimalBig.getValue())); + } @Test(expected = DatabaseException.class) public void primitiveDeserializeWrongTypeMap() { @@ -1250,6 +1279,22 @@ public int getValue() { } } + private static class BigIntegerBean { + private String value; + + public BigInteger getValue() { + return value == null ? null : new BigInteger(value); + } + } + + private static class BigDecimalBean { + private String value; + + public BigDecimal getValue() { + return value == null ? null : new BigDecimal(value); + } + } + private static class BooleanBean { private boolean value; diff --git a/src/test/java/com/google/firebase/database/snapshot/NodeTest.java b/src/test/java/com/google/firebase/database/snapshot/NodeTest.java index 22e6d6db8..0806bd03a 100644 --- a/src/test/java/com/google/firebase/database/snapshot/NodeTest.java +++ b/src/test/java/com/google/firebase/database/snapshot/NodeTest.java @@ -22,7 +22,11 @@ import com.google.firebase.database.MapBuilder; import com.google.firebase.database.core.Path; + +import java.math.BigDecimal; +import java.math.BigInteger; import java.util.Map; + import org.junit.Test; public class NodeTest { @@ -35,6 +39,8 @@ public void getHashWorksCorrectly() { .put("doubleNode", 4.5623) .put("stringNode", "hey guys") .put("boolNode", true) + .put("bigDecimalNode", BigDecimal.ONE) + .put("bigIntegerNode", BigInteger.TEN) .build(); Node node = NodeFromJSON(data); @@ -55,8 +61,16 @@ public void getHashWorksCorrectly() { hash = child.getHash(); assertEquals("E5z61QM0lN/U2WsOnusszCTkR8M=", hash); + child = node.getImmediateChild(ChildKey.fromString("bigDecimalNode")); + hash = child.getHash(); + assertEquals("CsHcc6ldMmpq3mgSeP+Q3XN48Ls=", hash); + + child = node.getImmediateChild(ChildKey.fromString("bigIntegerNode")); + hash = child.getHash(); + assertEquals("9dCcx7ZrW/Ul5TBwZRrABNgawlQ=", hash); + hash = node.getHash(); - assertEquals("6Mc4jFmNdrLVIlJJjz2/MakTK9I=", hash); + assertEquals("G8QKO+3CPOTwU4BIAU0XiPf8oFg=", hash); } @Test @@ -180,4 +194,18 @@ public void nodeFromJsonReturnsEmptyNodesWithoutPriority() { NodeFromJSON(new MapBuilder().put("dummy-node", null).put(".priority", "prio").build()); assertTrue(empty2.getPriority().isEmpty()); } + + @Test + public void bigDecimalsAreConvertedProperly() { + Node node = NodeFromJSON(BigDecimal.ONE); + assertTrue(node instanceof BigDecimalNode); + assertEquals(0, BigDecimal.ONE.compareTo((BigDecimal) node.getValue())); + } + + @Test + public void bigIntegersAreConvertedProperly() { + Node node = NodeFromJSON(BigInteger.TEN); + assertTrue(node instanceof BigIntegerNode); + assertEquals(0, BigInteger.TEN.compareTo((BigInteger) node.getValue())); + } }