aboutsummaryrefslogtreecommitdiffhomepage
path: root/java/util/src/main/java/com/google/protobuf/util/FieldMaskTree.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/util/src/main/java/com/google/protobuf/util/FieldMaskTree.java')
-rw-r--r--java/util/src/main/java/com/google/protobuf/util/FieldMaskTree.java138
1 files changed, 85 insertions, 53 deletions
diff --git a/java/util/src/main/java/com/google/protobuf/util/FieldMaskTree.java b/java/util/src/main/java/com/google/protobuf/util/FieldMaskTree.java
index dc2f4b84..4a13fb1d 100644
--- a/java/util/src/main/java/com/google/protobuf/util/FieldMaskTree.java
+++ b/java/util/src/main/java/com/google/protobuf/util/FieldMaskTree.java
@@ -34,10 +34,10 @@ import com.google.protobuf.Descriptors.Descriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
import com.google.protobuf.FieldMask;
import com.google.protobuf.Message;
-
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
+import java.util.SortedMap;
import java.util.TreeMap;
import java.util.logging.Logger;
@@ -59,26 +59,29 @@ import java.util.logging.Logger;
* intersection to two FieldMasks and traverse all fields specified by the
* FieldMask in a message tree.
*/
-class FieldMaskTree {
- private static final Logger logger =
- Logger.getLogger(FieldMaskTree.class.getName());
-
- private static final String FIELD_PATH_SEPARATOR_REGEX = "\\.";
+final class FieldMaskTree {
+ private static final Logger logger = Logger.getLogger(FieldMaskTree.class.getName());
+
+ private static final String FIELD_PATH_SEPARATOR_REGEX = "\\.";
- private static class Node {
- public TreeMap<String, Node> children = new TreeMap<String, Node>();
+ private static final class Node {
+ final SortedMap<String, Node> children = new TreeMap<String, Node>();
}
private final Node root = new Node();
- /** Creates an empty FieldMaskTree. */
- public FieldMaskTree() {}
-
- /** Creates a FieldMaskTree for a given FieldMask. */
- public FieldMaskTree(FieldMask mask) {
+ /**
+ * Creates an empty FieldMaskTree.
+ */
+ FieldMaskTree() {}
+
+ /**
+ * Creates a FieldMaskTree for a given FieldMask.
+ */
+ FieldMaskTree(FieldMask mask) {
mergeFromFieldMask(mask);
}
-
+
@Override
public String toString() {
return FieldMaskUtil.toString(toFieldMask());
@@ -94,7 +97,7 @@ class FieldMaskTree {
* Likewise, if the field path to add is a sub-path of an existing leaf node,
* nothing will be changed in the tree.
*/
- public FieldMaskTree addFieldPath(String path) {
+ FieldMaskTree addFieldPath(String path) {
String[] parts = path.split(FIELD_PATH_SEPARATOR_REGEX);
if (parts.length == 0) {
return this;
@@ -121,19 +124,21 @@ class FieldMaskTree {
node.children.clear();
return this;
}
-
+
/**
* Merges all field paths in a FieldMask into this tree.
*/
- public FieldMaskTree mergeFromFieldMask(FieldMask mask) {
+ FieldMaskTree mergeFromFieldMask(FieldMask mask) {
for (String path : mask.getPathsList()) {
addFieldPath(path);
}
return this;
}
- /** Converts this tree to a FieldMask. */
- public FieldMask toFieldMask() {
+ /**
+ * Converts this tree to a FieldMask.
+ */
+ FieldMask toFieldMask() {
if (root.children.isEmpty()) {
return FieldMask.getDefaultInstance();
}
@@ -142,24 +147,24 @@ class FieldMaskTree {
return FieldMask.newBuilder().addAllPaths(paths).build();
}
- /** Gathers all field paths in a sub-tree. */
+ /**
+ * Gathers all field paths in a sub-tree.
+ */
private void getFieldPaths(Node node, String path, List<String> paths) {
if (node.children.isEmpty()) {
paths.add(path);
return;
}
for (Entry<String, Node> entry : node.children.entrySet()) {
- String childPath = path.isEmpty()
- ? entry.getKey() : path + "." + entry.getKey();
+ String childPath = path.isEmpty() ? entry.getKey() : path + "." + entry.getKey();
getFieldPaths(entry.getValue(), childPath, paths);
}
}
/**
- * Adds the intersection of this tree with the given {@code path} to
- * {@code output}.
+ * Adds the intersection of this tree with the given {@code path} to {@code output}.
*/
- public void intersectFieldPath(String path, FieldMaskTree output) {
+ void intersectFieldPath(String path, FieldMaskTree output) {
if (root.children.isEmpty()) {
return;
}
@@ -190,14 +195,11 @@ class FieldMaskTree {
}
/**
- * Merges all fields specified by this FieldMaskTree from {@code source} to
- * {@code destination}.
+ * Merges all fields specified by this FieldMaskTree from {@code source} to {@code destination}.
*/
- public void merge(Message source, Message.Builder destination,
- FieldMaskUtil.MergeOptions options) {
+ void merge(Message source, Message.Builder destination, FieldMaskUtil.MergeOptions options) {
if (source.getDescriptorForType() != destination.getDescriptorForType()) {
- throw new IllegalArgumentException(
- "Cannot merge messages of different types.");
+ throw new IllegalArgumentException("Cannot merge messages of different types.");
}
if (root.children.isEmpty()) {
return;
@@ -205,33 +207,54 @@ class FieldMaskTree {
merge(root, "", source, destination, options);
}
- /** Merges all fields specified by a sub-tree from {@code source} to
- * {@code destination}.
+ /**
+ * Merges all fields specified by a sub-tree from {@code source} to {@code destination}.
*/
- private void merge(Node node, String path, Message source,
- Message.Builder destination, FieldMaskUtil.MergeOptions options) {
- assert source.getDescriptorForType() == destination.getDescriptorForType();
-
+ private void merge(
+ Node node,
+ String path,
+ Message source,
+ Message.Builder destination,
+ FieldMaskUtil.MergeOptions options) {
+ if (source.getDescriptorForType() != destination.getDescriptorForType()) {
+ throw new IllegalArgumentException(
+ String.format(
+ "source (%s) and destination (%s) descriptor must be equal",
+ source.getDescriptorForType(), destination.getDescriptorForType()));
+ }
+
Descriptor descriptor = source.getDescriptorForType();
for (Entry<String, Node> entry : node.children.entrySet()) {
- FieldDescriptor field =
- descriptor.findFieldByName(entry.getKey());
+ FieldDescriptor field = descriptor.findFieldByName(entry.getKey());
if (field == null) {
- logger.warning("Cannot find field \"" + entry.getKey()
- + "\" in message type " + descriptor.getFullName());
+ logger.warning(
+ "Cannot find field \""
+ + entry.getKey()
+ + "\" in message type "
+ + descriptor.getFullName());
continue;
}
if (!entry.getValue().children.isEmpty()) {
- if (field.isRepeated()
- || field.getJavaType() != FieldDescriptor.JavaType.MESSAGE) {
- logger.warning("Field \"" + field.getFullName() + "\" is not a "
- + "singluar message field and cannot have sub-fields.");
+ if (field.isRepeated() || field.getJavaType() != FieldDescriptor.JavaType.MESSAGE) {
+ logger.warning(
+ "Field \""
+ + field.getFullName()
+ + "\" is not a "
+ + "singluar message field and cannot have sub-fields.");
continue;
}
- String childPath = path.isEmpty()
- ? entry.getKey() : path + "." + entry.getKey();
- merge(entry.getValue(), childPath, (Message) source.getField(field),
- destination.getFieldBuilder(field), options);
+ if (!source.hasField(field) && !destination.hasField(field)) {
+ // If the message field is not present in both source and destination, skip recursing
+ // so we don't create unnecessary empty messages.
+ continue;
+ }
+ String childPath = path.isEmpty() ? entry.getKey() : path + "." + entry.getKey();
+ merge(
+ entry.getValue(),
+ childPath,
+ (Message) source.getField(field),
+ destination.getFieldBuilder(field),
+ options);
continue;
}
if (field.isRepeated()) {
@@ -245,13 +268,22 @@ class FieldMaskTree {
} else {
if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
if (options.replaceMessageFields()) {
- destination.setField(field, source.getField(field));
+ if (!source.hasField(field)) {
+ destination.clearField(field);
+ } else {
+ destination.setField(field, source.getField(field));
+ }
} else {
- destination.getFieldBuilder(field).mergeFrom(
- (Message) source.getField(field));
+ if (source.hasField(field)) {
+ destination.getFieldBuilder(field).mergeFrom((Message) source.getField(field));
+ }
}
} else {
- destination.setField(field, source.getField(field));
+ if (source.hasField(field) || !options.replacePrimitiveFields()) {
+ destination.setField(field, source.getField(field));
+ } else {
+ destination.clearField(field);
+ }
}
}
}