25
25
import java .util .List ;
26
26
import javax .annotation .Nullable ;
27
27
28
- /**
29
- * Utility helper functions to work with {@link com.google.protobuf.FieldMask}.
30
- */
28
+ /** Utility helper functions to work with {@link com.google.protobuf.FieldMask}. */
31
29
public final class FieldMaskUtil {
32
30
private static final String FIELD_PATH_SEPARATOR = "," ;
33
31
private static final String FIELD_PATH_SEPARATOR_REGEX = "," ;
34
32
private static final String FIELD_SEPARATOR_REGEX = "\\ ." ;
35
33
36
34
private FieldMaskUtil () {}
37
35
38
- /**
39
- * Converts a FieldMask to a string.
40
- */
36
+ /** Converts a FieldMask to a string. */
41
37
public static String toString (FieldMask fieldMask ) {
42
38
// TODO: Consider using com.google.common.base.Joiner here instead.
43
39
StringBuilder result = new StringBuilder ();
@@ -57,9 +53,7 @@ public static String toString(FieldMask fieldMask) {
57
53
return result .toString ();
58
54
}
59
55
60
- /**
61
- * Parses from a string to a FieldMask.
62
- */
56
+ /** Parses from a string to a FieldMask. */
63
57
public static FieldMask fromString (String value ) {
64
58
// TODO: Consider using com.google.common.base.Splitter here instead.
65
59
return fromStringList (Arrays .asList (value .split (FIELD_PATH_SEPARATOR_REGEX )));
@@ -145,8 +139,8 @@ public static FieldMask fromFieldNumbers(
145
139
}
146
140
147
141
/**
148
- * Converts a field mask to a Proto3 JSON string, that is converting from snake case to camel
149
- * case and joining all paths into one string with commas.
142
+ * Converts a field mask to a Proto3 JSON string, that is converting from snake case to camel case
143
+ * and joining all paths into one string with commas.
150
144
*/
151
145
public static String toJsonString (FieldMask fieldMask ) {
152
146
List <String > paths = new ArrayList <String >(fieldMask .getPathsCount ());
@@ -175,18 +169,14 @@ public static FieldMask fromJsonString(String value) {
175
169
return builder .build ();
176
170
}
177
171
178
- /**
179
- * Checks whether paths in a given fields mask are valid.
180
- */
172
+ /** Checks whether paths in a given fields mask are valid. */
181
173
public static boolean isValid (Class <? extends Message > type , FieldMask fieldMask ) {
182
174
Descriptor descriptor = Internal .getDefaultInstance (type ).getDescriptorForType ();
183
175
184
176
return isValid (descriptor , fieldMask );
185
177
}
186
178
187
- /**
188
- * Checks whether paths in a given fields mask are valid.
189
- */
179
+ /** Checks whether paths in a given fields mask are valid. */
190
180
public static boolean isValid (Descriptor descriptor , FieldMask fieldMask ) {
191
181
for (String path : fieldMask .getPathsList ()) {
192
182
if (!isValid (descriptor , path )) {
@@ -196,9 +186,7 @@ public static boolean isValid(Descriptor descriptor, FieldMask fieldMask) {
196
186
return true ;
197
187
}
198
188
199
- /**
200
- * Checks whether a given field path is valid.
201
- */
189
+ /** Checks whether a given field path is valid. */
202
190
public static boolean isValid (Class <? extends Message > type , String path ) {
203
191
Descriptor descriptor = Internal .getDefaultInstance (type ).getDescriptorForType ();
204
192
@@ -229,17 +217,14 @@ public static boolean isValid(@Nullable Descriptor descriptor, String path) {
229
217
}
230
218
231
219
/**
232
- * Converts a FieldMask to its canonical form. In the canonical form of a
233
- * FieldMask, all field paths are sorted alphabetically and redundant field
234
- * paths are removed.
220
+ * Converts a FieldMask to its canonical form. In the canonical form of a FieldMask, all field
221
+ * paths are sorted alphabetically and redundant field paths are removed.
235
222
*/
236
223
public static FieldMask normalize (FieldMask mask ) {
237
224
return new FieldMaskTree (mask ).toFieldMask ();
238
225
}
239
226
240
- /**
241
- * Creates a union of two or more FieldMasks.
242
- */
227
+ /** Creates a union of two or more FieldMasks. */
243
228
public static FieldMask union (
244
229
FieldMask firstMask , FieldMask secondMask , FieldMask ... otherMasks ) {
245
230
FieldMaskTree maskTree = new FieldMaskTree (firstMask ).mergeFromFieldMask (secondMask );
@@ -265,9 +250,7 @@ public static FieldMask subtract(
265
250
return maskTree .toFieldMask ();
266
251
}
267
252
268
- /**
269
- * Calculates the intersection of two FieldMasks.
270
- */
253
+ /** Calculates the intersection of two FieldMasks. */
271
254
public static FieldMask intersection (FieldMask mask1 , FieldMask mask2 ) {
272
255
FieldMaskTree tree = new FieldMaskTree (mask1 );
273
256
FieldMaskTree result = new FieldMaskTree ();
@@ -277,9 +260,7 @@ public static FieldMask intersection(FieldMask mask1, FieldMask mask2) {
277
260
return result .toFieldMask ();
278
261
}
279
262
280
- /**
281
- * Options to customize merging behavior.
282
- */
263
+ /** Options to customize merging behavior. */
283
264
public static final class MergeOptions {
284
265
private boolean replaceMessageFields = false ;
285
266
private boolean replaceRepeatedFields = false ;
@@ -288,25 +269,25 @@ public static final class MergeOptions {
288
269
private boolean replacePrimitiveFields = false ;
289
270
290
271
/**
291
- * Whether to replace message fields (i.e., discard existing content in
292
- * destination message fields).
272
+ * Whether to replace message fields (i.e., discard existing content in destination message
273
+ * fields).
293
274
*/
294
275
public boolean replaceMessageFields () {
295
276
return replaceMessageFields ;
296
277
}
297
278
298
279
/**
299
- * Whether to replace repeated fields (i.e., discard existing content in
300
- * destination repeated fields).
280
+ * Whether to replace repeated fields (i.e., discard existing content in destination repeated
281
+ * fields).
301
282
*/
302
283
public boolean replaceRepeatedFields () {
303
284
return replaceRepeatedFields ;
304
285
}
305
286
306
287
/**
307
- * Whether to replace primitive (non-repeated and non-message) fields in
308
- * destination message fields with the source primitive fields (i.e., clear
309
- * destination field if source field is not set).
288
+ * Whether to replace primitive (non-repeated and non-message) fields in destination message
289
+ * fields with the source primitive fields (i.e., clear destination field if source field is not
290
+ * set).
310
291
*/
311
292
public boolean replacePrimitiveFields () {
312
293
return replacePrimitiveFields ;
@@ -365,20 +346,66 @@ public static void merge(
365
346
new FieldMaskTree (mask ).merge (source , destination , options );
366
347
}
367
348
368
- /**
369
- * Merges fields specified by a FieldMask from one message to another.
370
- */
349
+ /** Merges fields specified by a FieldMask from one message to another. */
371
350
public static void merge (FieldMask mask , Message source , Message .Builder destination ) {
372
351
merge (mask , source , destination , new MergeOptions ());
373
352
}
374
353
354
+ /** Options to customize trimming behavior. */
355
+ public static final class TrimOptions {
356
+ private boolean retainPrimitiveFieldUnsetState = false ;
357
+
358
+ /** Whether the unset state of primitive fields should be retained when trimming. */
359
+ public boolean retainPrimitiveFieldUnsetState () {
360
+ return retainPrimitiveFieldUnsetState ;
361
+ }
362
+
363
+ /**
364
+ * Specify whether the unset state of primitive fields should be retained when trimming.
365
+ * Defaults to false.
366
+ *
367
+ * <p>If true, unset primitive fields indicated by the field mask will remain unset.
368
+ *
369
+ * <p>If false, unset primitive fields indicated by the field mask will be set to their default
370
+ * values.
371
+ */
372
+ @ CanIgnoreReturnValue
373
+ public TrimOptions setRetainPrimitiveFieldUnsetState (boolean value ) {
374
+ retainPrimitiveFieldUnsetState = value ;
375
+ return this ;
376
+ }
377
+ }
378
+
375
379
/**
376
- * Returns the result of keeping only the masked fields of the given proto.
380
+ * Returns the result of keeping only the masked fields of the given proto with the specified trim
381
+ * options.
382
+ *
383
+ * <p>Note that the behavior with the default {@link TrimOptions} is for unset primitive fields
384
+ * indicated in the field mask to be explicitly set to their default values. Use {@code new
385
+ * TrimOptions().setRetainPrimitiveFieldUnsetState(true)} to retain the unset state of primitive
386
+ * fields.
377
387
*/
378
388
@ SuppressWarnings ("unchecked" )
379
- public static <P extends Message > P trim (FieldMask mask , P source ) {
380
- Message .Builder destination = source .newBuilderForType ();
381
- merge (mask , source , destination );
389
+ public static <P extends Message > P trim (FieldMask mask , P source , TrimOptions options ) {
390
+ Message .Builder destination = source .newBuilderForType ();
391
+ merge (
392
+ mask ,
393
+ source ,
394
+ destination ,
395
+ new MergeOptions ().setReplacePrimitiveFields (options .retainPrimitiveFieldUnsetState ()));
382
396
return (P ) destination .build ();
383
397
}
398
+
399
+ /**
400
+ * Returns the result of keeping only the masked fields of the given proto.
401
+ *
402
+ * <p>This method is equivalent to {@link #trim(FieldMask, Message, TrimOptions)} with default
403
+ * {@link TrimOptions}.
404
+ *
405
+ * <p>Note that unset primitive fields indicated in the field mask will be explicitly set to their
406
+ * default values.
407
+ */
408
+ public static <P extends Message > P trim (FieldMask mask , P source ) {
409
+ return trim (mask , source , new TrimOptions ());
410
+ }
384
411
}
0 commit comments