Skip to content

Commit 2750928

Browse files
Merge
2 parents 618f903 + 94844bb commit 2750928

File tree

2 files changed

+255
-4
lines changed
  • firebase-firestore/src

2 files changed

+255
-4
lines changed
Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
// Copyright 2020 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.firebase.firestore.model.value;
16+
17+
import static com.google.firebase.firestore.util.Assert.fail;
18+
19+
import androidx.annotation.Nullable;
20+
import com.google.common.base.Splitter;
21+
import com.google.firebase.firestore.util.Util;
22+
import com.google.firestore.v1.ArrayValue;
23+
import com.google.firestore.v1.MapValue;
24+
import com.google.firestore.v1.Value;
25+
import com.google.protobuf.Timestamp;
26+
import com.google.type.LatLng;
27+
import java.util.Iterator;
28+
import java.util.List;
29+
import java.util.Map;
30+
import java.util.TreeMap;
31+
32+
// TODO(mrschmidt): Make package-private
33+
public class ProtoValues {
34+
35+
public static int typeOrder(Value value) {
36+
37+
switch (value.getValueTypeCase()) {
38+
case NULL_VALUE:
39+
return FieldValue.TYPE_ORDER_NULL;
40+
case BOOLEAN_VALUE:
41+
return FieldValue.TYPE_ORDER_BOOLEAN;
42+
case INTEGER_VALUE:
43+
return FieldValue.TYPE_ORDER_NUMBER;
44+
case DOUBLE_VALUE:
45+
return FieldValue.TYPE_ORDER_NUMBER;
46+
case TIMESTAMP_VALUE:
47+
return FieldValue.TYPE_ORDER_TIMESTAMP;
48+
case STRING_VALUE:
49+
return FieldValue.TYPE_ORDER_STRING;
50+
case BYTES_VALUE:
51+
return FieldValue.TYPE_ORDER_BLOB;
52+
case REFERENCE_VALUE:
53+
return FieldValue.TYPE_ORDER_REFERENCE;
54+
case GEO_POINT_VALUE:
55+
return FieldValue.TYPE_ORDER_GEOPOINT;
56+
case ARRAY_VALUE:
57+
return FieldValue.TYPE_ORDER_ARRAY;
58+
case MAP_VALUE:
59+
return FieldValue.TYPE_ORDER_OBJECT;
60+
default:
61+
throw fail("Invalid value type: " + value.getValueTypeCase());
62+
}
63+
}
64+
65+
/** Returns whether `value` is non-null and corresponds to the given type order. */
66+
public static boolean isType(@Nullable Value value, int typeOrder) {
67+
return value != null && typeOrder(value) == typeOrder;
68+
}
69+
70+
public static boolean equals(Value left, Value right) {
71+
int leftType = typeOrder(left);
72+
int rightType = typeOrder(right);
73+
if (leftType != rightType) {
74+
return false;
75+
}
76+
77+
switch (leftType) {
78+
case FieldValue.TYPE_ORDER_NUMBER:
79+
return numberEquals(left, right);
80+
case FieldValue.TYPE_ORDER_ARRAY:
81+
return arrayEquals(left, right);
82+
case FieldValue.TYPE_ORDER_OBJECT:
83+
return objectEquals(left, right);
84+
default:
85+
return left.equals(right);
86+
}
87+
}
88+
89+
private static boolean numberEquals(Value left, Value right) {
90+
if (left.getValueTypeCase() == Value.ValueTypeCase.INTEGER_VALUE
91+
&& right.getValueTypeCase() == Value.ValueTypeCase.INTEGER_VALUE) {
92+
return left.equals(right);
93+
} else if (left.getValueTypeCase() == Value.ValueTypeCase.DOUBLE_VALUE
94+
&& right.getValueTypeCase() == Value.ValueTypeCase.DOUBLE_VALUE) {
95+
return Double.doubleToLongBits(left.getDoubleValue())
96+
== Double.doubleToLongBits(right.getDoubleValue());
97+
}
98+
99+
return false;
100+
}
101+
102+
private static boolean arrayEquals(Value left, Value right) {
103+
ArrayValue leftArray = left.getArrayValue();
104+
ArrayValue rightArray = right.getArrayValue();
105+
106+
if (leftArray.getValuesCount() != rightArray.getValuesCount()) {
107+
return false;
108+
}
109+
110+
for (int i = 0; i < leftArray.getValuesCount(); ++i) {
111+
if (!equals(leftArray.getValues(i), rightArray.getValues(i))) {
112+
return false;
113+
}
114+
}
115+
116+
return true;
117+
}
118+
119+
private static boolean objectEquals(Value left, Value right) {
120+
MapValue leftMap = left.getMapValue();
121+
MapValue rightMap = right.getMapValue();
122+
123+
if (leftMap.getFieldsCount() != rightMap.getFieldsCount()) {
124+
return false;
125+
}
126+
127+
for (Map.Entry<String, Value> entry : leftMap.getFieldsMap().entrySet()) {
128+
Value otherEntry = rightMap.getFieldsMap().get(entry.getKey());
129+
if (!entry.getValue().equals(otherEntry)) {
130+
return false;
131+
}
132+
}
133+
134+
return true;
135+
}
136+
137+
public static int compare(Value left, Value right) {
138+
int leftType = typeOrder(left);
139+
int rightType = typeOrder(right);
140+
141+
if (leftType != rightType) {
142+
return Util.compareIntegers(leftType, rightType);
143+
}
144+
145+
switch (leftType) {
146+
case FieldValue.TYPE_ORDER_NULL:
147+
return 0;
148+
case FieldValue.TYPE_ORDER_BOOLEAN:
149+
return Util.compareBooleans(left.getBooleanValue(), right.getBooleanValue());
150+
case FieldValue.TYPE_ORDER_NUMBER:
151+
return compareNumbers(left, right);
152+
case FieldValue.TYPE_ORDER_TIMESTAMP:
153+
return compareTimestamps(left.getTimestampValue(), right.getTimestampValue());
154+
case FieldValue.TYPE_ORDER_STRING:
155+
return left.getStringValue().compareTo(right.getStringValue());
156+
case FieldValue.TYPE_ORDER_BLOB:
157+
return Util.compareByteStrings(left.getBytesValue(), right.getBytesValue());
158+
case FieldValue.TYPE_ORDER_REFERENCE:
159+
return compareReferences(left.getReferenceValue(), right.getReferenceValue());
160+
case FieldValue.TYPE_ORDER_GEOPOINT:
161+
return compareGeoPoints(left.getGeoPointValue(), right.getGeoPointValue());
162+
case FieldValue.TYPE_ORDER_ARRAY:
163+
return compareArrays(left.getArrayValue(), right.getArrayValue());
164+
case FieldValue.TYPE_ORDER_OBJECT:
165+
return compareMaps(left.getMapValue(), right.getMapValue());
166+
default:
167+
throw fail("Invalid value type: " + leftType);
168+
}
169+
}
170+
171+
private static int compareNumbers(Value left, Value right) {
172+
if (left.getValueTypeCase() == Value.ValueTypeCase.DOUBLE_VALUE) {
173+
double thisDouble = left.getDoubleValue();
174+
if (right.getValueTypeCase() == Value.ValueTypeCase.DOUBLE_VALUE) {
175+
return Util.compareDoubles(thisDouble, right.getDoubleValue());
176+
} else if (right.getValueTypeCase() == Value.ValueTypeCase.INTEGER_VALUE) {
177+
return Util.compareMixed(thisDouble, right.getIntegerValue());
178+
}
179+
} else if (left.getValueTypeCase() == Value.ValueTypeCase.INTEGER_VALUE) {
180+
long thisLong = left.getIntegerValue();
181+
if (right.getValueTypeCase() == Value.ValueTypeCase.INTEGER_VALUE) {
182+
return Util.compareLongs(thisLong, right.getIntegerValue());
183+
} else if (right.getValueTypeCase() == Value.ValueTypeCase.DOUBLE_VALUE) {
184+
return -1 * Util.compareMixed(right.getDoubleValue(), thisLong);
185+
}
186+
}
187+
188+
throw fail("Unexpected values: %s vs %s", left, right);
189+
}
190+
191+
private static int compareTimestamps(Timestamp left, Timestamp right) {
192+
int comparison = Util.compareLongs(left.getSeconds(), right.getSeconds());
193+
if (comparison != 0) {
194+
return comparison;
195+
}
196+
return Util.compareIntegers(left.getNanos(), right.getNanos());
197+
}
198+
199+
private static int compareReferences(String leftPath, String rightPath) {
200+
List<String> leftSegments = Splitter.on('/').splitToList(leftPath);
201+
List<String> rightSegments = Splitter.on('/').splitToList(rightPath);
202+
int minLength = Math.min(leftSegments.size(), rightSegments.size());
203+
for (int i = 0; i < minLength; i++) {
204+
int cmp = leftSegments.get(i).compareTo(rightSegments.get(i));
205+
if (cmp != 0) {
206+
return cmp;
207+
}
208+
}
209+
return Util.compareIntegers(leftSegments.size(), rightSegments.size());
210+
}
211+
212+
private static int compareGeoPoints(LatLng left, LatLng right) {
213+
int comparison = Util.compareDoubles(left.getLatitude(), right.getLatitude());
214+
if (comparison == 0) {
215+
return Util.compareDoubles(left.getLongitude(), right.getLongitude());
216+
}
217+
return comparison;
218+
}
219+
220+
private static int compareArrays(ArrayValue left, ArrayValue right) {
221+
int minLength = Math.min(left.getValuesCount(), right.getValuesCount());
222+
for (int i = 0; i < minLength; i++) {
223+
int cmp = compare(left.getValues(i), right.getValues(i));
224+
if (cmp != 0) {
225+
return cmp;
226+
}
227+
}
228+
return Util.compareIntegers(left.getValuesCount(), right.getValuesCount());
229+
}
230+
231+
private static int compareMaps(MapValue left, MapValue right) {
232+
Iterator<Map.Entry<String, Value>> iterator1 =
233+
new TreeMap<>(left.getFieldsMap()).entrySet().iterator();
234+
Iterator<Map.Entry<String, Value>> iterator2 =
235+
new TreeMap<>(right.getFieldsMap()).entrySet().iterator();
236+
while (iterator1.hasNext() && iterator2.hasNext()) {
237+
Map.Entry<String, Value> entry1 = iterator1.next();
238+
Map.Entry<String, Value> entry2 = iterator2.next();
239+
int keyCompare = entry1.getKey().compareTo(entry2.getKey());
240+
if (keyCompare != 0) {
241+
return keyCompare;
242+
}
243+
int valueCompare = compare(entry1.getValue(), entry2.getValue());
244+
if (valueCompare != 0) {
245+
return valueCompare;
246+
}
247+
}
248+
249+
// Only equal if both iterators are exhausted.
250+
return Util.compareBooleans(iterator1.hasNext(), iterator2.hasNext());
251+
}
252+
}

firebase-firestore/src/roboUtil/java/com/google/firebase/firestore/Values.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import com.google.firebase.firestore.model.DatabaseId;
1919
import com.google.firebase.firestore.model.DocumentKey;
2020
import com.google.firestore.v1.ArrayValue;
21+
import com.google.firestore.v1.MapValue;
2122
import com.google.firestore.v1.Value;
2223
import com.google.protobuf.NullValue;
2324
import com.google.type.LatLng;
@@ -75,8 +76,7 @@ public static Value valueOf(Object o) {
7576
}
7677
return Value.newBuilder().setArrayValue(list).build();
7778
} else if (o instanceof Map) {
78-
com.google.firestore.v1.MapValue.Builder builder =
79-
com.google.firestore.v1.MapValue.newBuilder();
79+
MapValue.Builder builder = MapValue.newBuilder();
8080
for (Map.Entry<String, Object> entry : ((Map<String, Object>) o).entrySet()) {
8181
builder.putFields(entry.getKey(), valueOf(entry.getValue()));
8282
}
@@ -88,8 +88,7 @@ public static Value valueOf(Object o) {
8888

8989
/** Creates a MapValue from a list of key/value arguments. */
9090
public static Value map(Object... entries) {
91-
com.google.firestore.v1.MapValue.Builder builder =
92-
com.google.firestore.v1.MapValue.newBuilder();
91+
MapValue.Builder builder = MapValue.newBuilder();
9392
for (int i = 0; i < entries.length; i += 2) {
9493
builder.putFields((String) entries[i], valueOf(entries[i + 1]));
9594
}

0 commit comments

Comments
 (0)