1b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer// Protocol Buffers - Google's data interchange format
2b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer// Copyright 2008 Google Inc.  All rights reserved.
3b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer// https://developers.google.com/protocol-buffers/
4b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer//
5b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer// Redistribution and use in source and binary forms, with or without
6b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer// modification, are permitted provided that the following conditions are
7b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer// met:
8b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer//
9b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer//     * Redistributions of source code must retain the above copyright
10b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer// notice, this list of conditions and the following disclaimer.
11b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer//     * Redistributions in binary form must reproduce the above
12b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer// copyright notice, this list of conditions and the following disclaimer
13b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer// in the documentation and/or other materials provided with the
14b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer// distribution.
15b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer//     * Neither the name of Google Inc. nor the names of its
16b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer// contributors may be used to endorse or promote products derived from
17b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer// this software without specific prior written permission.
18b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer//
19b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer
31b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammerpackage com.google.protobuf.util;
32b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer
33b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammerimport com.google.protobuf.Descriptors.Descriptor;
34b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammerimport com.google.protobuf.Descriptors.FieldDescriptor;
35b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammerimport com.google.protobuf.FieldMask;
36b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammerimport com.google.protobuf.Message;
37b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer
38b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammerimport java.util.ArrayList;
39b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammerimport java.util.List;
40b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammerimport java.util.Map.Entry;
41b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammerimport java.util.TreeMap;
42b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammerimport java.util.logging.Logger;
43b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer
44b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer/**
45b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer * A tree representation of a FieldMask. Each leaf node in this tree represent
46b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer * a field path in the FieldMask.
47b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer *
48b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer * <p>For example, FieldMask "foo.bar,foo.baz,bar.baz" as a tree will be:
49b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer * <pre>
50b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer *   [root] -+- foo -+- bar
51b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer *           |       |
52b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer *           |       +- baz
53b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer *           |
54b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer *           +- bar --- baz
55b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer * </pre>
56b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer *
57b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer * <p>By representing FieldMasks with this tree structure we can easily convert
58b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer * a FieldMask to a canonical form, merge two FieldMasks, calculate the
59b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer * intersection to two FieldMasks and traverse all fields specified by the
60b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer * FieldMask in a message tree.
61b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer */
62b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammerclass FieldMaskTree {
63b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer  private static final Logger logger = Logger.getLogger(FieldMaskTree.class.getName());
64b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer
65b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer  private static final String FIELD_PATH_SEPARATOR_REGEX = "\\.";
66b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer
67b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer  private static class Node {
68b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    public TreeMap<String, Node> children = new TreeMap<String, Node>();
69b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer  }
70b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer
71b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer  private final Node root = new Node();
72b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer
73b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer  /** Creates an empty FieldMaskTree. */
74b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer  public FieldMaskTree() {}
75b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer
76b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer  /** Creates a FieldMaskTree for a given FieldMask. */
77b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer  public FieldMaskTree(FieldMask mask) {
78b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    mergeFromFieldMask(mask);
79b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer  }
80b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer
81b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer  @Override
82b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer  public String toString() {
83b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    return FieldMaskUtil.toString(toFieldMask());
84b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer  }
85b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer
86b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer  /**
87b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer   * Adds a field path to the tree. In a FieldMask, every field path matches the
88b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer   * specified field as well as all its sub-fields. For example, a field path
89b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer   * "foo.bar" matches field "foo.bar" and also "foo.bar.baz", etc. When adding
90b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer   * a field path to the tree, redundant sub-paths will be removed. That is,
91b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer   * after adding "foo.bar" to the tree, "foo.bar.baz" will be removed if it
92b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer   * exists, which will turn the tree node for "foo.bar" to a leaf node.
93b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer   * Likewise, if the field path to add is a sub-path of an existing leaf node,
94b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer   * nothing will be changed in the tree.
95b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer   */
96b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer  public FieldMaskTree addFieldPath(String path) {
97b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    String[] parts = path.split(FIELD_PATH_SEPARATOR_REGEX);
98b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    if (parts.length == 0) {
99b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer      return this;
100b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    }
101b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    Node node = root;
102b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    boolean createNewBranch = false;
103b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    // Find the matching node in the tree.
104b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    for (String part : parts) {
105b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer      // Check whether the path matches an existing leaf node.
106b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer      if (!createNewBranch && node != root && node.children.isEmpty()) {
107b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer        // The path to add is a sub-path of an existing leaf node.
108b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer        return this;
109b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer      }
110b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer      if (node.children.containsKey(part)) {
111b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer        node = node.children.get(part);
112b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer      } else {
113b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer        createNewBranch = true;
114b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer        Node tmp = new Node();
115b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer        node.children.put(part, tmp);
116b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer        node = tmp;
117b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer      }
118b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    }
119b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    // Turn the matching node into a leaf node (i.e., remove sub-paths).
120b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    node.children.clear();
121b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    return this;
122b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer  }
123b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer
124b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer  /**
125b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer   * Merges all field paths in a FieldMask into this tree.
126b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer   */
127b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer  public FieldMaskTree mergeFromFieldMask(FieldMask mask) {
128b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    for (String path : mask.getPathsList()) {
129b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer      addFieldPath(path);
130b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    }
131b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    return this;
132b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer  }
133b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer
134b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer  /** Converts this tree to a FieldMask. */
135b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer  public FieldMask toFieldMask() {
136b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    if (root.children.isEmpty()) {
137b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer      return FieldMask.getDefaultInstance();
138b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    }
139b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    List<String> paths = new ArrayList<String>();
140b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    getFieldPaths(root, "", paths);
141b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    return FieldMask.newBuilder().addAllPaths(paths).build();
142b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer  }
143b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer
144b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer  /** Gathers all field paths in a sub-tree. */
145b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer  private void getFieldPaths(Node node, String path, List<String> paths) {
146b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    if (node.children.isEmpty()) {
147b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer      paths.add(path);
148b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer      return;
149b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    }
150b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    for (Entry<String, Node> entry : node.children.entrySet()) {
151b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer      String childPath = path.isEmpty() ? entry.getKey() : path + "." + entry.getKey();
152b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer      getFieldPaths(entry.getValue(), childPath, paths);
153b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    }
154b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer  }
155b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer
156b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer  /**
157b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer   * Adds the intersection of this tree with the given {@code path} to
158b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer   * {@code output}.
159b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer   */
160b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer  public void intersectFieldPath(String path, FieldMaskTree output) {
161b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    if (root.children.isEmpty()) {
162b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer      return;
163b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    }
164b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    String[] parts = path.split(FIELD_PATH_SEPARATOR_REGEX);
165b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    if (parts.length == 0) {
166b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer      return;
167b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    }
168b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    Node node = root;
169b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    for (String part : parts) {
170b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer      if (node != root && node.children.isEmpty()) {
171b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer        // The given path is a sub-path of an existing leaf node in the tree.
172b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer        output.addFieldPath(path);
173b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer        return;
174b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer      }
175b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer      if (node.children.containsKey(part)) {
176b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer        node = node.children.get(part);
177b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer      } else {
178b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer        return;
179b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer      }
180b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    }
181b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    // We found a matching node for the path. All leaf children of this matching
182b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    // node is in the intersection.
183b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    List<String> paths = new ArrayList<String>();
184b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    getFieldPaths(node, path, paths);
185b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    for (String value : paths) {
186b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer      output.addFieldPath(value);
187b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    }
188b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer  }
189b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer
190b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer  /**
191b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer   * Merges all fields specified by this FieldMaskTree from {@code source} to
192b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer   * {@code destination}.
193b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer   */
194b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer  public void merge(
195b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer      Message source, Message.Builder destination, FieldMaskUtil.MergeOptions options) {
196b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    if (source.getDescriptorForType() != destination.getDescriptorForType()) {
197b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer      throw new IllegalArgumentException("Cannot merge messages of different types.");
198b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    }
199b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    if (root.children.isEmpty()) {
200b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer      return;
201b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    }
202b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    merge(root, "", source, destination, options);
203b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer  }
204b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer
205b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer  /** Merges all fields specified by a sub-tree from {@code source} to
206b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer   * {@code destination}.
207b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer   */
208b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer  private void merge(
209b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer      Node node,
210b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer      String path,
211b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer      Message source,
212b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer      Message.Builder destination,
213b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer      FieldMaskUtil.MergeOptions options) {
214b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    assert source.getDescriptorForType() == destination.getDescriptorForType();
215b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer
216b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    Descriptor descriptor = source.getDescriptorForType();
217b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    for (Entry<String, Node> entry : node.children.entrySet()) {
218b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer      FieldDescriptor field = descriptor.findFieldByName(entry.getKey());
219b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer      if (field == null) {
220b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer        logger.warning(
221b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer            "Cannot find field \""
222b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer                + entry.getKey()
223b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer                + "\" in message type "
224b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer                + descriptor.getFullName());
225b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer        continue;
226b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer      }
227b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer      if (!entry.getValue().children.isEmpty()) {
228b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer        if (field.isRepeated() || field.getJavaType() != FieldDescriptor.JavaType.MESSAGE) {
229b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer          logger.warning(
230b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer              "Field \""
231b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer                  + field.getFullName()
232b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer                  + "\" is not a "
233b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer                  + "singluar message field and cannot have sub-fields.");
234b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer          continue;
235b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer        }
236b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer        String childPath = path.isEmpty() ? entry.getKey() : path + "." + entry.getKey();
237b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer        merge(
238b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer            entry.getValue(),
239b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer            childPath,
240b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer            (Message) source.getField(field),
241b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer            destination.getFieldBuilder(field),
242b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer            options);
243b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer        continue;
244b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer      }
245b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer      if (field.isRepeated()) {
246b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer        if (options.replaceRepeatedFields()) {
247b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer          destination.setField(field, source.getField(field));
248b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer        } else {
249b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer          for (Object element : (List) source.getField(field)) {
250b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer            destination.addRepeatedField(field, element);
251b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer          }
252b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer        }
253b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer      } else {
254b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer        if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
255b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer          if (options.replaceMessageFields()) {
256b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer            if (!source.hasField(field)) {
257b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer              destination.clearField(field);
258b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer            } else {
259b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer              destination.setField(field, source.getField(field));
260b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer            }
261b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer          } else {
262b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer            if (source.hasField(field)) {
263b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer              destination.getFieldBuilder(field).mergeFrom((Message) source.getField(field));
264b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer            }
265b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer          }
266b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer        } else {
267b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer          if (source.hasField(field) || !options.replacePrimitiveFields()) {
268b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer            destination.setField(field, source.getField(field));
269b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer          } else {
270b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer            destination.clearField(field);
271b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer          }
272b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer        }
273b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer      }
274b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer    }
275b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer  }
276b0575e93e4c39dec69365b850088a1eb7f82c5b3Tamas Berghammer}
277