31 package com.google.protobuf.util;
33 import com.
google.errorprone.annotations.CanIgnoreReturnValue;
38 import java.util.ArrayList;
39 import java.util.List;
40 import java.util.Map.Entry;
41 import java.util.SortedMap;
42 import java.util.TreeMap;
43 import java.util.logging.Logger;
63 final class FieldMaskTree {
64 private static final Logger logger = Logger.getLogger(FieldMaskTree.class.getName());
66 private static final String FIELD_PATH_SEPARATOR_REGEX =
"\\.";
68 private static final class Node {
69 final SortedMap<String, Node> children =
new TreeMap<String, Node>();
83 mergeFromFieldMask(
mask);
87 public String toString() {
88 return FieldMaskUtil.toString(toFieldMask());
100 FieldMaskTree addFieldPath(String
path) {
101 String[] parts =
path.split(FIELD_PATH_SEPARATOR_REGEX);
102 if (parts.length == 0) {
106 boolean createNewBranch =
false;
108 for (String part : parts) {
110 if (!createNewBranch && node !=
root && node.children.isEmpty()) {
114 if (node.children.containsKey(part)) {
115 node = node.children.get(part);
117 createNewBranch =
true;
118 Node tmp =
new Node();
119 node.children.put(part, tmp);
124 node.children.clear();
129 @CanIgnoreReturnValue
131 for (String
path :
mask.getPathsList()) {
141 if (
root.children.isEmpty()) {
144 List<String> paths =
new ArrayList<String>();
145 getFieldPaths(
root,
"", paths);
146 return FieldMask.newBuilder().addAllPaths(paths).build();
152 private void getFieldPaths(Node node, String
path, List<String> paths) {
153 if (node.children.isEmpty()) {
157 for (Entry<String, Node> entry : node.children.entrySet()) {
158 String childPath =
path.isEmpty() ? entry.getKey() :
path +
"." + entry.getKey();
159 getFieldPaths(entry.getValue(), childPath, paths);
166 void intersectFieldPath(String
path, FieldMaskTree
output) {
167 if (
root.children.isEmpty()) {
170 String[] parts =
path.split(FIELD_PATH_SEPARATOR_REGEX);
171 if (parts.length == 0) {
175 for (String part : parts) {
176 if (node !=
root && node.children.isEmpty()) {
181 if (node.children.containsKey(part)) {
182 node = node.children.get(part);
189 List<String> paths =
new ArrayList<String>();
190 getFieldPaths(node,
path, paths);
191 for (String
value : paths) {
199 void merge(Message
source, Message.Builder destination, FieldMaskUtil.MergeOptions
options) {
200 if (
source.getDescriptorForType() != destination.getDescriptorForType()) {
201 throw new IllegalArgumentException(
"Cannot merge messages of different types.");
203 if (
root.children.isEmpty()) {
216 Message.Builder destination,
217 FieldMaskUtil.MergeOptions
options) {
218 if (
source.getDescriptorForType() != destination.getDescriptorForType()) {
219 throw new IllegalArgumentException(
221 "source (%s) and destination (%s) descriptor must be equal",
222 source.getDescriptorForType(), destination.getDescriptorForType()));
226 for (Entry<String, Node> entry : node.children.entrySet()) {
230 "Cannot find field \""
232 +
"\" in message type "
236 if (!entry.getValue().children.isEmpty()) {
240 +
field.getFullName()
242 +
"singular message field and cannot have sub-fields.");
250 String childPath =
path.isEmpty() ? entry.getKey() :
path +
"." + entry.getKey();
251 Message.Builder childBuilder = ((Message) destination.getField(
field)).toBuilder();
253 destination.setField(
field, childBuilder.buildPartial());
256 if (
field.isRepeated()) {
257 if (
options.replaceRepeatedFields()) {
260 for (Object element : (List)
source.getField(
field)) {
261 destination.addRepeatedField(
field, element);
266 if (
options.replaceMessageFields()) {
268 destination.clearField(
field);
274 destination.setField(
276 ((Message) destination.getField(
field))
286 destination.clearField(
field);