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