Skip to content

Commit baa0a89

Browse files
Add an ObjectValue builder (#1150)
1 parent 331ec37 commit baa0a89

File tree

9 files changed

+148
-91
lines changed

9 files changed

+148
-91
lines changed

firebase-firestore/src/main/java/com/google/firebase/firestore/UserDataConverter.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ public ParsedUpdateData parseUpdateData(Map<String, Object> data) {
117117

118118
ParseAccumulator accumulator = new ParseAccumulator(UserData.Source.Update);
119119
ParseContext context = accumulator.rootContext();
120-
ObjectValue updateData = ObjectValue.emptyObject();
120+
ObjectValue.Builder updateData = ObjectValue.newBuilder();
121121

122122
for (Entry<String, Object> entry : data.entrySet()) {
123123
FieldPath fieldPath =
@@ -134,12 +134,12 @@ public ParsedUpdateData parseUpdateData(Map<String, Object> data) {
134134
convertAndParseFieldData(fieldValue, context.childContext(fieldPath));
135135
if (parsedValue != null) {
136136
context.addToFieldMask(fieldPath);
137-
updateData = updateData.set(fieldPath, parsedValue);
137+
updateData.set(fieldPath, parsedValue);
138138
}
139139
}
140140
}
141141

142-
return accumulator.toUpdateData(updateData);
142+
return accumulator.toUpdateData(updateData.build());
143143
}
144144

145145
/**
@@ -155,7 +155,7 @@ public ParsedUpdateData parseUpdateData(List<Object> fieldsAndValues) {
155155

156156
ParseAccumulator accumulator = new ParseAccumulator(UserData.Source.Update);
157157
ParseContext context = accumulator.rootContext();
158-
ObjectValue updateData = ObjectValue.emptyObject();
158+
ObjectValue.Builder updateData = ObjectValue.newBuilder();
159159

160160
Iterator<Object> iterator = fieldsAndValues.iterator();
161161
while (iterator.hasNext()) {
@@ -185,12 +185,12 @@ public ParsedUpdateData parseUpdateData(List<Object> fieldsAndValues) {
185185
convertAndParseFieldData(fieldValue, context.childContext(parsedField));
186186
if (parsedValue != null) {
187187
context.addToFieldMask(parsedField);
188-
updateData = updateData.set(parsedField, parsedValue);
188+
updateData.set(parsedField, parsedValue);
189189
}
190190
}
191191
}
192192

193-
return accumulator.toUpdateData(updateData);
193+
return accumulator.toUpdateData(updateData.build());
194194
}
195195

196196
/** Parse a "query value" (e.g. value in a where filter or a value in a cursor bound). */

firebase-firestore/src/main/java/com/google/firebase/firestore/model/Document.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,14 +95,14 @@ public ObjectValue getData() {
9595
if (objectValue == null) {
9696
hardAssert(proto != null && converter != null, "Expected proto and converter to be non-null");
9797

98-
ObjectValue result = ObjectValue.emptyObject();
98+
ObjectValue.Builder result = ObjectValue.newBuilder();
9999
for (Map.Entry<String, com.google.firestore.v1.Value> entry :
100100
proto.getFieldsMap().entrySet()) {
101101
FieldPath path = FieldPath.fromSingleSegment(entry.getKey());
102102
FieldValue value = converter.apply(entry.getValue());
103-
result = result.set(path, value);
103+
result.set(path, value);
104104
}
105-
objectValue = result;
105+
objectValue = result.build();
106106

107107
// Once objectValue is computed, values inside the fieldValueCache are no longer accessed.
108108
fieldValueCache = null;

firebase-firestore/src/main/java/com/google/firebase/firestore/model/mutation/PatchMutation.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -151,16 +151,17 @@ private ObjectValue patchDocument(@Nullable MaybeDocument maybeDoc) {
151151
}
152152

153153
private ObjectValue patchObject(ObjectValue obj) {
154+
ObjectValue.Builder builder = obj.toBuilder();
154155
for (FieldPath path : mask.getMask()) {
155156
if (!path.isEmpty()) {
156157
FieldValue newValue = value.get(path);
157158
if (newValue == null) {
158-
obj = obj.delete(path);
159+
builder.delete(path);
159160
} else {
160-
obj = obj.set(path, newValue);
161+
builder.set(path, newValue);
161162
}
162163
}
163164
}
164-
return obj;
165+
return builder.build();
165166
}
166167
}

firebase-firestore/src/main/java/com/google/firebase/firestore/model/mutation/TransformMutation.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ public MaybeDocument applyToLocalView(
124124
@Nullable
125125
@Override
126126
public ObjectValue extractBaseValue(@Nullable MaybeDocument maybeDoc) {
127-
ObjectValue baseObject = null;
127+
ObjectValue.Builder baseObject = null;
128128

129129
for (FieldTransform transform : fieldTransforms) {
130130
FieldValue existingValue = null;
@@ -135,14 +135,14 @@ public ObjectValue extractBaseValue(@Nullable MaybeDocument maybeDoc) {
135135
FieldValue coercedValue = transform.getOperation().computeBaseValue(existingValue);
136136
if (coercedValue != null) {
137137
if (baseObject == null) {
138-
baseObject = ObjectValue.emptyObject().set(transform.getFieldPath(), coercedValue);
138+
baseObject = ObjectValue.newBuilder().set(transform.getFieldPath(), coercedValue);
139139
} else {
140140
baseObject = baseObject.set(transform.getFieldPath(), coercedValue);
141141
}
142142
}
143143
}
144144

145-
return baseObject;
145+
return baseObject != null ? baseObject.build() : null;
146146
}
147147

148148
/**
@@ -227,11 +227,12 @@ private ObjectValue transformObject(ObjectValue objectValue, List<FieldValue> tr
227227
hardAssert(
228228
transformResults.size() == fieldTransforms.size(), "Transform results length mismatch.");
229229

230+
ObjectValue.Builder builder = objectValue.toBuilder();
230231
for (int i = 0; i < fieldTransforms.size(); i++) {
231232
FieldTransform fieldTransform = fieldTransforms.get(i);
232233
FieldPath fieldPath = fieldTransform.getFieldPath();
233-
objectValue = objectValue.set(fieldPath, transformResults.get(i));
234+
builder.set(fieldPath, transformResults.get(i));
234235
}
235-
return objectValue;
236+
return builder.build();
236237
}
237238
}

firebase-firestore/src/main/java/com/google/firebase/firestore/model/value/ObjectValue.java

Lines changed: 77 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ public static ObjectValue emptyObject() {
4949
return EMPTY_INSTANCE;
5050
}
5151

52+
/** Returns a new Builder instance that is based on an empty object. */
53+
public static Builder newBuilder() {
54+
return new Builder(ImmutableSortedMap.Builder.emptyMap(Util.<String>comparator()));
55+
}
56+
5257
private final ImmutableSortedMap<String, FieldValue> internalValue;
5358

5459
private ObjectValue(ImmutableSortedMap<String, FieldValue> value) {
@@ -141,55 +146,6 @@ public boolean equals(Object other) {
141146
&& internalValue.equals(((ObjectValue) other).internalValue);
142147
}
143148

144-
/**
145-
* Returns a new ObjectValue with the field at the named path set to value.
146-
*
147-
* @param path The field path to set.
148-
* @param value The value to set.
149-
* @return A new ObjectValue with the field set.
150-
*/
151-
public ObjectValue set(FieldPath path, FieldValue value) {
152-
hardAssert(!path.isEmpty(), "Cannot set field for empty path on ObjectValue");
153-
String childName = path.getFirstSegment();
154-
if (path.length() == 1) {
155-
return setChild(childName, value);
156-
} else {
157-
FieldValue child = internalValue.get(childName);
158-
ObjectValue obj;
159-
if (child instanceof ObjectValue) {
160-
obj = (ObjectValue) child;
161-
} else {
162-
obj = emptyObject();
163-
}
164-
ObjectValue newChild = obj.set(path.popFirst(), value);
165-
return setChild(childName, newChild);
166-
}
167-
}
168-
169-
/**
170-
* Returns an ObjectValue with the field path deleted. If there is no field at the specified path
171-
* nothing is changed.
172-
*
173-
* @param path The field path to remove
174-
* @return An ObjectValue with the field path removed.
175-
*/
176-
public ObjectValue delete(FieldPath path) {
177-
hardAssert(!path.isEmpty(), "Cannot delete field for empty path on ObjectValue");
178-
String childName = path.getFirstSegment();
179-
if (path.length() == 1) {
180-
return fromImmutableMap(internalValue.remove(childName));
181-
} else {
182-
FieldValue child = internalValue.get(childName);
183-
if (child instanceof ObjectValue) {
184-
ObjectValue newChild = ((ObjectValue) child).delete(path.popFirst());
185-
return setChild(childName, newChild);
186-
} else {
187-
// Don't actually change a primitive value to an object for a delete.
188-
return this;
189-
}
190-
}
191-
}
192-
193149
/**
194150
* Returns the value at the given path or null.
195151
*
@@ -207,7 +163,77 @@ public ObjectValue delete(FieldPath path) {
207163
return current;
208164
}
209165

210-
private ObjectValue setChild(String childName, FieldValue value) {
211-
return fromImmutableMap(internalValue.insert(childName, value));
166+
/** Creates a ObjectValue.Builder instance that is based on the current value. */
167+
public Builder toBuilder() {
168+
return new Builder(internalValue);
169+
}
170+
171+
/**
172+
* An ObjectValue.Builder provides APIs to set and delete fields from an ObjectValue. All
173+
* operations mutate the existing instance.
174+
*/
175+
public static class Builder {
176+
177+
private ImmutableSortedMap<String, FieldValue> internalValue;
178+
179+
Builder(ImmutableSortedMap<String, FieldValue> internalValue) {
180+
this.internalValue = internalValue;
181+
}
182+
183+
/**
184+
* Sets the field to the provided value.
185+
*
186+
* @param path The field path to set.
187+
* @param value The value to set.
188+
* @return The current Builder instance.
189+
*/
190+
public Builder set(FieldPath path, FieldValue value) {
191+
hardAssert(!path.isEmpty(), "Cannot set field for empty path on ObjectValue");
192+
String childName = path.getFirstSegment();
193+
if (path.length() == 1) {
194+
internalValue = internalValue.insert(childName, value);
195+
} else {
196+
FieldValue child = internalValue.get(childName);
197+
ObjectValue obj;
198+
if (child instanceof ObjectValue) {
199+
obj = (ObjectValue) child;
200+
} else {
201+
obj = emptyObject();
202+
}
203+
ObjectValue newChild = obj.toBuilder().set(path.popFirst(), value).build();
204+
internalValue = internalValue.insert(childName, newChild);
205+
}
206+
207+
return this;
208+
}
209+
210+
/**
211+
* Removes the field at the current path. If there is no field at the specified path nothing is
212+
* changed.
213+
*
214+
* @param path The field path to remove
215+
* @return The current Builder instance.
216+
*/
217+
public Builder delete(FieldPath path) {
218+
hardAssert(!path.isEmpty(), "Cannot delete field for empty path on ObjectValue");
219+
String childName = path.getFirstSegment();
220+
if (path.length() == 1) {
221+
internalValue = internalValue.remove(childName);
222+
} else {
223+
FieldValue child = internalValue.get(childName);
224+
if (child instanceof ObjectValue) {
225+
ObjectValue newChild = ((ObjectValue) child).toBuilder().delete(path.popFirst()).build();
226+
internalValue = internalValue.insert(childName, newChild);
227+
} else {
228+
// Don't actually change a primitive value to an object for a delete.
229+
}
230+
}
231+
232+
return this;
233+
}
234+
235+
public ObjectValue build() {
236+
return new ObjectValue(internalValue);
237+
}
212238
}
213239
}

firebase-firestore/src/main/java/com/google/firebase/firestore/remote/RemoteSerializer.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -369,13 +369,13 @@ private ObjectValue decodeMapValue(MapValue value) {
369369
// involve creating a temporary map.
370370

371371
public ObjectValue decodeFields(Map<String, com.google.firestore.v1.Value> fields) {
372-
ObjectValue result = ObjectValue.emptyObject();
372+
ObjectValue.Builder result = ObjectValue.newBuilder();
373373
for (Map.Entry<String, com.google.firestore.v1.Value> entry : fields.entrySet()) {
374374
FieldPath path = FieldPath.fromSingleSegment(entry.getKey());
375375
FieldValue value = decodeValue(entry.getValue());
376-
result = result.set(path, value);
376+
result.set(path, value);
377377
}
378-
return result;
378+
return result.build();
379379
}
380380

381381
// Documents

0 commit comments

Comments
 (0)