35
35
import com .google .firebase .firestore .model .mutation .FieldMask ;
36
36
import com .google .firebase .firestore .model .mutation .NumericIncrementTransformOperation ;
37
37
import com .google .firebase .firestore .model .mutation .ServerTimestampOperation ;
38
- import com .google .firebase .firestore .model .value .ArrayValue ;
39
- import com .google .firebase .firestore .model .value .BlobValue ;
40
- import com .google .firebase .firestore .model .value .BooleanValue ;
41
- import com .google .firebase .firestore .model .value .DoubleValue ;
42
38
import com .google .firebase .firestore .model .value .FieldValue ;
43
- import com .google .firebase .firestore .model .value .GeoPointValue ;
44
- import com .google .firebase .firestore .model .value .IntegerValue ;
45
- import com .google .firebase .firestore .model .value .NullValue ;
46
39
import com .google .firebase .firestore .model .value .NumberValue ;
47
40
import com .google .firebase .firestore .model .value .ObjectValue ;
48
- import com .google .firebase .firestore .model .value .ReferenceValue ;
49
- import com .google .firebase .firestore .model .value .StringValue ;
50
- import com .google .firebase .firestore .model .value .TimestampValue ;
51
41
import com .google .firebase .firestore .util .Assert ;
52
42
import com .google .firebase .firestore .util .CustomClassMapper ;
53
43
import com .google .firebase .firestore .util .Util ;
44
+ import com .google .firestore .v1 .ArrayValue ;
45
+ import com .google .firestore .v1 .MapValue ;
46
+ import com .google .firestore .v1 .Value ;
47
+ import com .google .protobuf .NullValue ;
48
+ import com .google .type .LatLng ;
54
49
import java .util .ArrayList ;
55
50
import java .util .Date ;
56
- import java .util .HashMap ;
57
51
import java .util .Iterator ;
58
52
import java .util .List ;
59
53
import java .util .Map ;
@@ -130,8 +124,7 @@ public ParsedUpdateData parseUpdateData(Map<String, Object> data) {
130
124
context .addToFieldMask (fieldPath );
131
125
} else {
132
126
@ Nullable
133
- FieldValue parsedValue =
134
- convertAndParseFieldData (fieldValue , context .childContext (fieldPath ));
127
+ Value parsedValue = convertAndParseFieldData (fieldValue , context .childContext (fieldPath ));
135
128
if (parsedValue != null ) {
136
129
context .addToFieldMask (fieldPath );
137
130
updateData .set (fieldPath , parsedValue );
@@ -181,8 +174,7 @@ public ParsedUpdateData parseUpdateData(List<Object> fieldsAndValues) {
181
174
// Add it to the field mask, but don't add anything to updateData.
182
175
context .addToFieldMask (parsedField );
183
176
} else {
184
- FieldValue parsedValue =
185
- convertAndParseFieldData (fieldValue , context .childContext (parsedField ));
177
+ Value parsedValue = convertAndParseFieldData (fieldValue , context .childContext (parsedField ));
186
178
if (parsedValue != null ) {
187
179
context .addToFieldMask (parsedField );
188
180
updateData .set (parsedField , parsedValue );
@@ -209,16 +201,16 @@ public FieldValue parseQueryValue(Object input, boolean allowArrays) {
209
201
new ParseAccumulator (
210
202
allowArrays ? UserData .Source .ArrayArgument : UserData .Source .Argument );
211
203
212
- @ Nullable FieldValue parsed = convertAndParseFieldData (input , accumulator .rootContext ());
204
+ @ Nullable Value parsed = convertAndParseFieldData (input , accumulator .rootContext ());
213
205
hardAssert (parsed != null , "Parsed data should not be null." );
214
206
hardAssert (
215
207
accumulator .getFieldTransforms ().isEmpty (),
216
208
"Field transforms should have been disallowed." );
217
- return parsed ;
209
+ return FieldValue . valueOf ( parsed ) ;
218
210
}
219
211
220
212
/** Converts a POJO to native types and then parses it into model types. */
221
- private FieldValue convertAndParseFieldData (Object input , ParseContext context ) {
213
+ private Value convertAndParseFieldData (Object input , ParseContext context ) {
222
214
Object converted = CustomClassMapper .convertToPlainJavaTypes (input );
223
215
return parseData (converted , context );
224
216
}
@@ -239,7 +231,7 @@ private ObjectValue convertAndParseDocumentData(Object input, ParseContext conte
239
231
}
240
232
241
233
Object converted = CustomClassMapper .convertToPlainJavaTypes (input );
242
- FieldValue value = parseData (converted , context );
234
+ FieldValue value = FieldValue . valueOf ( parseData (converted , context ) );
243
235
244
236
if (!(value instanceof ObjectValue )) {
245
237
throw new IllegalArgumentException (badDocReason + "of type: " + Util .typeName (input ));
@@ -257,7 +249,7 @@ private ObjectValue convertAndParseDocumentData(Object input, ParseContext conte
257
249
* not be included in the resulting parsed data.
258
250
*/
259
251
@ Nullable
260
- private FieldValue parseData (Object input , ParseContext context ) {
252
+ private Value parseData (Object input , ParseContext context ) {
261
253
if (input instanceof Map ) {
262
254
return parseMap ((Map <?, ?>) input , context );
263
255
@@ -291,43 +283,42 @@ private FieldValue parseData(Object input, ParseContext context) {
291
283
}
292
284
}
293
285
294
- private <K , V > ObjectValue parseMap (Map <K , V > map , ParseContext context ) {
295
- Map <String , FieldValue > result = new HashMap <>();
296
-
286
+ private <K , V > Value parseMap (Map <K , V > map , ParseContext context ) {
297
287
if (map .isEmpty ()) {
298
288
if (context .getPath () != null && !context .getPath ().isEmpty ()) {
299
289
context .addToFieldMask (context .getPath ());
300
290
}
301
- return ObjectValue . emptyObject ();
291
+ return Value . newBuilder (). setMapValue ( MapValue . getDefaultInstance ()). build ();
302
292
} else {
293
+ MapValue .Builder mapBuilder = MapValue .newBuilder ();
303
294
for (Entry <K , V > entry : map .entrySet ()) {
304
295
if (!(entry .getKey () instanceof String )) {
305
296
throw context .createError (
306
297
String .format ("Non-String Map key (%s) is not allowed" , entry .getValue ()));
307
298
}
308
299
String key = (String ) entry .getKey ();
309
- @ Nullable FieldValue parsedValue = parseData (entry .getValue (), context .childContext (key ));
300
+ @ Nullable Value parsedValue = parseData (entry .getValue (), context .childContext (key ));
310
301
if (parsedValue != null ) {
311
- result . put (key , parsedValue );
302
+ mapBuilder . putFields (key , parsedValue );
312
303
}
313
304
}
305
+ return Value .newBuilder ().setMapValue (mapBuilder ).build ();
314
306
}
315
- return ObjectValue .fromMap (result );
316
307
}
317
308
318
- private <T > ArrayValue parseList (List <T > list , ParseContext context ) {
319
- List < FieldValue > result = new ArrayList <>( list . size () );
309
+ private <T > Value parseList (List <T > list , ParseContext context ) {
310
+ ArrayValue . Builder arrayBuilder = ArrayValue . newBuilder ( );
320
311
int entryIndex = 0 ;
321
312
for (T entry : list ) {
322
- @ Nullable FieldValue parsedEntry = parseData (entry , context .childContext (entryIndex ));
313
+ @ Nullable Value parsedEntry = parseData (entry , context .childContext (entryIndex ));
323
314
if (parsedEntry == null ) {
324
315
// Just include nulls in the array for fields being replaced with a sentinel.
325
- parsedEntry = NullValue .nullValue ();
316
+ parsedEntry = Value . newBuilder (). setNullValue ( NullValue .NULL_VALUE ). build ();
326
317
}
327
- result . add (parsedEntry );
318
+ arrayBuilder . addValues (parsedEntry );
328
319
entryIndex ++;
329
320
}
330
- return ArrayValue . fromList ( result );
321
+ return Value . newBuilder (). setArrayValue ( arrayBuilder ). build ( );
331
322
}
332
323
333
324
/**
@@ -398,35 +389,37 @@ private void parseSentinelFieldValue(
398
389
* @return The parsed value, or {@code null} if the value was a FieldValue sentinel that should
399
390
* not be included in the resulting parsed data.
400
391
*/
401
- private FieldValue parseScalarValue (Object input , ParseContext context ) {
392
+ private Value parseScalarValue (Object input , ParseContext context ) {
402
393
if (input == null ) {
403
- return NullValue .nullValue ();
394
+ return Value . newBuilder (). setNullValue ( NullValue .NULL_VALUE ). build ();
404
395
} else if (input instanceof Integer ) {
405
- return IntegerValue . valueOf ( ((Integer ) input ).longValue () );
396
+ return Value . newBuilder (). setIntegerValue ((Integer ) input ).build ( );
406
397
} else if (input instanceof Long ) {
407
- return IntegerValue . valueOf ( ((Long ) input ));
398
+ return Value . newBuilder (). setIntegerValue ((Long ) input ). build ( );
408
399
} else if (input instanceof Float ) {
409
- return DoubleValue . valueOf ((( Float ) input ).doubleValue ());
400
+ return Value . newBuilder (). setDoubleValue ((( Float ) input ).doubleValue ()). build ( );
410
401
} else if (input instanceof Double ) {
411
- return DoubleValue . valueOf (( Double ) input );
402
+ return Value . newBuilder (). setDoubleValue (( Double ) input ). build ( );
412
403
} else if (input instanceof Boolean ) {
413
- return BooleanValue . valueOf (( Boolean ) input );
404
+ return Value . newBuilder (). setBooleanValue (( Boolean ) input ). build ( );
414
405
} else if (input instanceof String ) {
415
- return StringValue . valueOf (( String ) input );
406
+ return Value . newBuilder (). setStringValue (( String ) input ). build ( );
416
407
} else if (input instanceof Date ) {
417
- return TimestampValue .valueOf (new Timestamp ((Date ) input ));
408
+ Timestamp timestamp = new Timestamp ((Date ) input );
409
+ return parseTimestamp (timestamp );
418
410
} else if (input instanceof Timestamp ) {
419
411
Timestamp timestamp = (Timestamp ) input ;
420
- long seconds = timestamp .getSeconds ();
421
- // Firestore backend truncates precision down to microseconds. To ensure offline mode works
422
- // the same with regards to truncation, perform the truncation immediately without waiting for
423
- // the backend to do that.
424
- int truncatedNanoseconds = timestamp .getNanoseconds () / 1000 * 1000 ;
425
- return TimestampValue .valueOf (new Timestamp (seconds , truncatedNanoseconds ));
412
+ return parseTimestamp (timestamp );
426
413
} else if (input instanceof GeoPoint ) {
427
- return GeoPointValue .valueOf ((GeoPoint ) input );
414
+ GeoPoint geoPoint = (GeoPoint ) input ;
415
+ return Value .newBuilder ()
416
+ .setGeoPointValue (
417
+ LatLng .newBuilder ()
418
+ .setLatitude (geoPoint .getLatitude ())
419
+ .setLongitude (geoPoint .getLongitude ()))
420
+ .build ();
428
421
} else if (input instanceof Blob ) {
429
- return BlobValue . valueOf (( Blob ) input );
422
+ return Value . newBuilder (). setBytesValue ((( Blob ) input ). toByteString ()). build ( );
430
423
} else if (input instanceof DocumentReference ) {
431
424
DocumentReference ref = (DocumentReference ) input ;
432
425
// TODO: Rework once pre-converter is ported to Android.
@@ -442,14 +435,35 @@ private FieldValue parseScalarValue(Object input, ParseContext context) {
442
435
databaseId .getDatabaseId ()));
443
436
}
444
437
}
445
- return ReferenceValue .valueOf (databaseId , ref .getKey ());
438
+ return Value .newBuilder ()
439
+ .setReferenceValue (
440
+ String .format (
441
+ "projects/%s/databases/%s/documents/%s" ,
442
+ databaseId .getProjectId (),
443
+ databaseId .getDatabaseId (),
444
+ ((DocumentReference ) input ).getPath ()))
445
+ .build ();
446
446
} else if (input .getClass ().isArray ()) {
447
447
throw context .createError ("Arrays are not supported; use a List instead" );
448
448
} else {
449
449
throw context .createError ("Unsupported type: " + Util .typeName (input ));
450
450
}
451
451
}
452
452
453
+ private Value parseTimestamp (Timestamp timestamp ) {
454
+ // Firestore backend truncates precision down to microseconds. To ensure offline mode works
455
+ // the same with regards to truncation, perform the truncation immediately without waiting for
456
+ // the backend to do that.
457
+ int truncatedNanoseconds = timestamp .getNanoseconds () / 1000 * 1000 ;
458
+
459
+ return Value .newBuilder ()
460
+ .setTimestampValue (
461
+ com .google .protobuf .Timestamp .newBuilder ()
462
+ .setSeconds (timestamp .getSeconds ())
463
+ .setNanos (truncatedNanoseconds ))
464
+ .build ();
465
+ }
466
+
453
467
private List <FieldValue > parseArrayTransformElements (List <Object > elements ) {
454
468
ParseAccumulator accumulator = new ParseAccumulator (UserData .Source .Argument );
455
469
@@ -460,7 +474,7 @@ private List<FieldValue> parseArrayTransformElements(List<Object> elements) {
460
474
// being unioned or removed are not considered writes since they cannot
461
475
// contain any FieldValue sentinels, etc.
462
476
ParseContext context = accumulator .rootContext ();
463
- result .add (convertAndParseFieldData (element , context .childContext (i )));
477
+ result .add (FieldValue . valueOf ( convertAndParseFieldData (element , context .childContext (i ) )));
464
478
}
465
479
return result ;
466
480
}
0 commit comments