1*1b3f573fSAndroid Build Coastguard Worker #region Copyright notice and license 2*1b3f573fSAndroid Build Coastguard Worker // Protocol Buffers - Google's data interchange format 3*1b3f573fSAndroid Build Coastguard Worker // Copyright 2015 Google Inc. All rights reserved. 4*1b3f573fSAndroid Build Coastguard Worker // https://developers.google.com/protocol-buffers/ 5*1b3f573fSAndroid Build Coastguard Worker // 6*1b3f573fSAndroid Build Coastguard Worker // Redistribution and use in source and binary forms, with or without 7*1b3f573fSAndroid Build Coastguard Worker // modification, are permitted provided that the following conditions are 8*1b3f573fSAndroid Build Coastguard Worker // met: 9*1b3f573fSAndroid Build Coastguard Worker // 10*1b3f573fSAndroid Build Coastguard Worker // * Redistributions of source code must retain the above copyright 11*1b3f573fSAndroid Build Coastguard Worker // notice, this list of conditions and the following disclaimer. 12*1b3f573fSAndroid Build Coastguard Worker // * Redistributions in binary form must reproduce the above 13*1b3f573fSAndroid Build Coastguard Worker // copyright notice, this list of conditions and the following disclaimer 14*1b3f573fSAndroid Build Coastguard Worker // in the documentation and/or other materials provided with the 15*1b3f573fSAndroid Build Coastguard Worker // distribution. 16*1b3f573fSAndroid Build Coastguard Worker // * Neither the name of Google Inc. nor the names of its 17*1b3f573fSAndroid Build Coastguard Worker // contributors may be used to endorse or promote products derived from 18*1b3f573fSAndroid Build Coastguard Worker // this software without specific prior written permission. 19*1b3f573fSAndroid Build Coastguard Worker // 20*1b3f573fSAndroid Build Coastguard Worker // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21*1b3f573fSAndroid Build Coastguard Worker // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22*1b3f573fSAndroid Build Coastguard Worker // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23*1b3f573fSAndroid Build Coastguard Worker // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24*1b3f573fSAndroid Build Coastguard Worker // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25*1b3f573fSAndroid Build Coastguard Worker // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26*1b3f573fSAndroid Build Coastguard Worker // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27*1b3f573fSAndroid Build Coastguard Worker // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28*1b3f573fSAndroid Build Coastguard Worker // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29*1b3f573fSAndroid Build Coastguard Worker // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30*1b3f573fSAndroid Build Coastguard Worker // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31*1b3f573fSAndroid Build Coastguard Worker #endregion 32*1b3f573fSAndroid Build Coastguard Worker 33*1b3f573fSAndroid Build Coastguard Worker using System.Collections; 34*1b3f573fSAndroid Build Coastguard Worker using System.Collections.Generic; 35*1b3f573fSAndroid Build Coastguard Worker using System.Diagnostics; 36*1b3f573fSAndroid Build Coastguard Worker using Google.Protobuf.Reflection; 37*1b3f573fSAndroid Build Coastguard Worker using Google.Protobuf.WellKnownTypes; 38*1b3f573fSAndroid Build Coastguard Worker 39*1b3f573fSAndroid Build Coastguard Worker namespace Google.Protobuf 40*1b3f573fSAndroid Build Coastguard Worker { 41*1b3f573fSAndroid Build Coastguard Worker /// <summary> 42*1b3f573fSAndroid Build Coastguard Worker /// <para>A tree representation of a FieldMask. Each leaf node in this tree represent 43*1b3f573fSAndroid Build Coastguard Worker /// a field path in the FieldMask.</para> 44*1b3f573fSAndroid Build Coastguard Worker /// 45*1b3f573fSAndroid Build Coastguard Worker /// <para>For example, FieldMask "foo.bar,foo.baz,bar.baz" as a tree will be:</para> 46*1b3f573fSAndroid Build Coastguard Worker /// <code> 47*1b3f573fSAndroid Build Coastguard Worker /// [root] -+- foo -+- bar 48*1b3f573fSAndroid Build Coastguard Worker /// | | 49*1b3f573fSAndroid Build Coastguard Worker /// | +- baz 50*1b3f573fSAndroid Build Coastguard Worker /// | 51*1b3f573fSAndroid Build Coastguard Worker /// +- bar --- baz 52*1b3f573fSAndroid Build Coastguard Worker /// </code> 53*1b3f573fSAndroid Build Coastguard Worker /// 54*1b3f573fSAndroid Build Coastguard Worker /// <para>By representing FieldMasks with this tree structure we can easily convert 55*1b3f573fSAndroid Build Coastguard Worker /// a FieldMask to a canonical form, merge two FieldMasks, calculate the 56*1b3f573fSAndroid Build Coastguard Worker /// intersection to two FieldMasks and traverse all fields specified by the 57*1b3f573fSAndroid Build Coastguard Worker /// FieldMask in a message tree.</para> 58*1b3f573fSAndroid Build Coastguard Worker /// </summary> 59*1b3f573fSAndroid Build Coastguard Worker internal sealed class FieldMaskTree 60*1b3f573fSAndroid Build Coastguard Worker { 61*1b3f573fSAndroid Build Coastguard Worker private const char FIELD_PATH_SEPARATOR = '.'; 62*1b3f573fSAndroid Build Coastguard Worker 63*1b3f573fSAndroid Build Coastguard Worker internal sealed class Node 64*1b3f573fSAndroid Build Coastguard Worker { 65*1b3f573fSAndroid Build Coastguard Worker public Dictionary<string, Node> Children { get; } = new Dictionary<string, Node>(); 66*1b3f573fSAndroid Build Coastguard Worker } 67*1b3f573fSAndroid Build Coastguard Worker 68*1b3f573fSAndroid Build Coastguard Worker private readonly Node root = new Node(); 69*1b3f573fSAndroid Build Coastguard Worker 70*1b3f573fSAndroid Build Coastguard Worker /// <summary> 71*1b3f573fSAndroid Build Coastguard Worker /// Creates an empty FieldMaskTree. 72*1b3f573fSAndroid Build Coastguard Worker /// </summary> FieldMaskTree()73*1b3f573fSAndroid Build Coastguard Worker public FieldMaskTree() 74*1b3f573fSAndroid Build Coastguard Worker { 75*1b3f573fSAndroid Build Coastguard Worker } 76*1b3f573fSAndroid Build Coastguard Worker 77*1b3f573fSAndroid Build Coastguard Worker /// <summary> 78*1b3f573fSAndroid Build Coastguard Worker /// Creates a FieldMaskTree for a given FieldMask. 79*1b3f573fSAndroid Build Coastguard Worker /// </summary> FieldMaskTree(FieldMask mask)80*1b3f573fSAndroid Build Coastguard Worker public FieldMaskTree(FieldMask mask) 81*1b3f573fSAndroid Build Coastguard Worker { 82*1b3f573fSAndroid Build Coastguard Worker MergeFromFieldMask(mask); 83*1b3f573fSAndroid Build Coastguard Worker } 84*1b3f573fSAndroid Build Coastguard Worker ToString()85*1b3f573fSAndroid Build Coastguard Worker public override string ToString() 86*1b3f573fSAndroid Build Coastguard Worker { 87*1b3f573fSAndroid Build Coastguard Worker return ToFieldMask().ToString(); 88*1b3f573fSAndroid Build Coastguard Worker } 89*1b3f573fSAndroid Build Coastguard Worker 90*1b3f573fSAndroid Build Coastguard Worker /// <summary> 91*1b3f573fSAndroid Build Coastguard Worker /// Adds a field path to the tree. In a FieldMask, every field path matches the 92*1b3f573fSAndroid Build Coastguard Worker /// specified field as well as all its sub-fields. For example, a field path 93*1b3f573fSAndroid Build Coastguard Worker /// "foo.bar" matches field "foo.bar" and also "foo.bar.baz", etc. When adding 94*1b3f573fSAndroid Build Coastguard Worker /// a field path to the tree, redundant sub-paths will be removed. That is, 95*1b3f573fSAndroid Build Coastguard Worker /// after adding "foo.bar" to the tree, "foo.bar.baz" will be removed if it 96*1b3f573fSAndroid Build Coastguard Worker /// exists, which will turn the tree node for "foo.bar" to a leaf node. 97*1b3f573fSAndroid Build Coastguard Worker /// Likewise, if the field path to add is a sub-path of an existing leaf node, 98*1b3f573fSAndroid Build Coastguard Worker /// nothing will be changed in the tree. 99*1b3f573fSAndroid Build Coastguard Worker /// </summary> AddFieldPath(string path)100*1b3f573fSAndroid Build Coastguard Worker public FieldMaskTree AddFieldPath(string path) 101*1b3f573fSAndroid Build Coastguard Worker { 102*1b3f573fSAndroid Build Coastguard Worker var parts = path.Split(FIELD_PATH_SEPARATOR); 103*1b3f573fSAndroid Build Coastguard Worker if (parts.Length == 0) 104*1b3f573fSAndroid Build Coastguard Worker { 105*1b3f573fSAndroid Build Coastguard Worker return this; 106*1b3f573fSAndroid Build Coastguard Worker } 107*1b3f573fSAndroid Build Coastguard Worker 108*1b3f573fSAndroid Build Coastguard Worker var node = root; 109*1b3f573fSAndroid Build Coastguard Worker var createNewBranch = false; 110*1b3f573fSAndroid Build Coastguard Worker 111*1b3f573fSAndroid Build Coastguard Worker // Find the matching node in the tree. 112*1b3f573fSAndroid Build Coastguard Worker foreach (var part in parts) 113*1b3f573fSAndroid Build Coastguard Worker { 114*1b3f573fSAndroid Build Coastguard Worker // Check whether the path matches an existing leaf node. 115*1b3f573fSAndroid Build Coastguard Worker if (!createNewBranch 116*1b3f573fSAndroid Build Coastguard Worker && node != root 117*1b3f573fSAndroid Build Coastguard Worker && node.Children.Count == 0) 118*1b3f573fSAndroid Build Coastguard Worker { 119*1b3f573fSAndroid Build Coastguard Worker // The path to add is a sub-path of an existing leaf node. 120*1b3f573fSAndroid Build Coastguard Worker return this; 121*1b3f573fSAndroid Build Coastguard Worker } 122*1b3f573fSAndroid Build Coastguard Worker 123*1b3f573fSAndroid Build Coastguard Worker Node childNode; 124*1b3f573fSAndroid Build Coastguard Worker if (!node.Children.TryGetValue(part, out childNode)) 125*1b3f573fSAndroid Build Coastguard Worker { 126*1b3f573fSAndroid Build Coastguard Worker createNewBranch = true; 127*1b3f573fSAndroid Build Coastguard Worker childNode = new Node(); 128*1b3f573fSAndroid Build Coastguard Worker node.Children.Add(part, childNode); 129*1b3f573fSAndroid Build Coastguard Worker } 130*1b3f573fSAndroid Build Coastguard Worker node = childNode; 131*1b3f573fSAndroid Build Coastguard Worker } 132*1b3f573fSAndroid Build Coastguard Worker 133*1b3f573fSAndroid Build Coastguard Worker // Turn the matching node into a leaf node (i.e., remove sub-paths). 134*1b3f573fSAndroid Build Coastguard Worker node.Children.Clear(); 135*1b3f573fSAndroid Build Coastguard Worker return this; 136*1b3f573fSAndroid Build Coastguard Worker } 137*1b3f573fSAndroid Build Coastguard Worker 138*1b3f573fSAndroid Build Coastguard Worker /// <summary> 139*1b3f573fSAndroid Build Coastguard Worker /// Merges all field paths in a FieldMask into this tree. 140*1b3f573fSAndroid Build Coastguard Worker /// </summary> MergeFromFieldMask(FieldMask mask)141*1b3f573fSAndroid Build Coastguard Worker public FieldMaskTree MergeFromFieldMask(FieldMask mask) 142*1b3f573fSAndroid Build Coastguard Worker { 143*1b3f573fSAndroid Build Coastguard Worker foreach (var path in mask.Paths) 144*1b3f573fSAndroid Build Coastguard Worker { 145*1b3f573fSAndroid Build Coastguard Worker AddFieldPath(path); 146*1b3f573fSAndroid Build Coastguard Worker } 147*1b3f573fSAndroid Build Coastguard Worker 148*1b3f573fSAndroid Build Coastguard Worker return this; 149*1b3f573fSAndroid Build Coastguard Worker } 150*1b3f573fSAndroid Build Coastguard Worker 151*1b3f573fSAndroid Build Coastguard Worker /// <summary> 152*1b3f573fSAndroid Build Coastguard Worker /// Converts this tree to a FieldMask. 153*1b3f573fSAndroid Build Coastguard Worker /// </summary> ToFieldMask()154*1b3f573fSAndroid Build Coastguard Worker public FieldMask ToFieldMask() 155*1b3f573fSAndroid Build Coastguard Worker { 156*1b3f573fSAndroid Build Coastguard Worker var mask = new FieldMask(); 157*1b3f573fSAndroid Build Coastguard Worker if (root.Children.Count != 0) 158*1b3f573fSAndroid Build Coastguard Worker { 159*1b3f573fSAndroid Build Coastguard Worker var paths = new List<string>(); 160*1b3f573fSAndroid Build Coastguard Worker GetFieldPaths(root, "", paths); 161*1b3f573fSAndroid Build Coastguard Worker mask.Paths.AddRange(paths); 162*1b3f573fSAndroid Build Coastguard Worker } 163*1b3f573fSAndroid Build Coastguard Worker 164*1b3f573fSAndroid Build Coastguard Worker return mask; 165*1b3f573fSAndroid Build Coastguard Worker } 166*1b3f573fSAndroid Build Coastguard Worker 167*1b3f573fSAndroid Build Coastguard Worker /// <summary> 168*1b3f573fSAndroid Build Coastguard Worker /// Gathers all field paths in a sub-tree. 169*1b3f573fSAndroid Build Coastguard Worker /// </summary> GetFieldPaths(Node node, string path, List<string> paths)170*1b3f573fSAndroid Build Coastguard Worker private void GetFieldPaths(Node node, string path, List<string> paths) 171*1b3f573fSAndroid Build Coastguard Worker { 172*1b3f573fSAndroid Build Coastguard Worker if (node.Children.Count == 0) 173*1b3f573fSAndroid Build Coastguard Worker { 174*1b3f573fSAndroid Build Coastguard Worker paths.Add(path); 175*1b3f573fSAndroid Build Coastguard Worker return; 176*1b3f573fSAndroid Build Coastguard Worker } 177*1b3f573fSAndroid Build Coastguard Worker 178*1b3f573fSAndroid Build Coastguard Worker foreach (var entry in node.Children) 179*1b3f573fSAndroid Build Coastguard Worker { 180*1b3f573fSAndroid Build Coastguard Worker var childPath = path.Length == 0 ? entry.Key : path + "." + entry.Key; 181*1b3f573fSAndroid Build Coastguard Worker GetFieldPaths(entry.Value, childPath, paths); 182*1b3f573fSAndroid Build Coastguard Worker } 183*1b3f573fSAndroid Build Coastguard Worker } 184*1b3f573fSAndroid Build Coastguard Worker 185*1b3f573fSAndroid Build Coastguard Worker /// <summary> 186*1b3f573fSAndroid Build Coastguard Worker /// Adds the intersection of this tree with the given <paramref name="path"/> to <paramref name="output"/>. 187*1b3f573fSAndroid Build Coastguard Worker /// </summary> IntersectFieldPath(string path, FieldMaskTree output)188*1b3f573fSAndroid Build Coastguard Worker public void IntersectFieldPath(string path, FieldMaskTree output) 189*1b3f573fSAndroid Build Coastguard Worker { 190*1b3f573fSAndroid Build Coastguard Worker if (root.Children.Count == 0) 191*1b3f573fSAndroid Build Coastguard Worker { 192*1b3f573fSAndroid Build Coastguard Worker return; 193*1b3f573fSAndroid Build Coastguard Worker } 194*1b3f573fSAndroid Build Coastguard Worker 195*1b3f573fSAndroid Build Coastguard Worker var parts = path.Split(FIELD_PATH_SEPARATOR); 196*1b3f573fSAndroid Build Coastguard Worker if (parts.Length == 0) 197*1b3f573fSAndroid Build Coastguard Worker { 198*1b3f573fSAndroid Build Coastguard Worker return; 199*1b3f573fSAndroid Build Coastguard Worker } 200*1b3f573fSAndroid Build Coastguard Worker 201*1b3f573fSAndroid Build Coastguard Worker var node = root; 202*1b3f573fSAndroid Build Coastguard Worker foreach (var part in parts) 203*1b3f573fSAndroid Build Coastguard Worker { 204*1b3f573fSAndroid Build Coastguard Worker if (node != root 205*1b3f573fSAndroid Build Coastguard Worker && node.Children.Count == 0) 206*1b3f573fSAndroid Build Coastguard Worker { 207*1b3f573fSAndroid Build Coastguard Worker // The given path is a sub-path of an existing leaf node in the tree. 208*1b3f573fSAndroid Build Coastguard Worker output.AddFieldPath(path); 209*1b3f573fSAndroid Build Coastguard Worker return; 210*1b3f573fSAndroid Build Coastguard Worker } 211*1b3f573fSAndroid Build Coastguard Worker 212*1b3f573fSAndroid Build Coastguard Worker if (!node.Children.TryGetValue(part, out node)) 213*1b3f573fSAndroid Build Coastguard Worker { 214*1b3f573fSAndroid Build Coastguard Worker return; 215*1b3f573fSAndroid Build Coastguard Worker } 216*1b3f573fSAndroid Build Coastguard Worker } 217*1b3f573fSAndroid Build Coastguard Worker 218*1b3f573fSAndroid Build Coastguard Worker // We found a matching node for the path. All leaf children of this matching 219*1b3f573fSAndroid Build Coastguard Worker // node is in the intersection. 220*1b3f573fSAndroid Build Coastguard Worker var paths = new List<string>(); 221*1b3f573fSAndroid Build Coastguard Worker GetFieldPaths(node, path, paths); 222*1b3f573fSAndroid Build Coastguard Worker foreach (var value in paths) 223*1b3f573fSAndroid Build Coastguard Worker { 224*1b3f573fSAndroid Build Coastguard Worker output.AddFieldPath(value); 225*1b3f573fSAndroid Build Coastguard Worker } 226*1b3f573fSAndroid Build Coastguard Worker } 227*1b3f573fSAndroid Build Coastguard Worker 228*1b3f573fSAndroid Build Coastguard Worker /// <summary> 229*1b3f573fSAndroid Build Coastguard Worker /// Merges all fields specified by this FieldMaskTree from <paramref name="source"/> to <paramref name="destination"/>. 230*1b3f573fSAndroid Build Coastguard Worker /// </summary> Merge(IMessage source, IMessage destination, FieldMask.MergeOptions options)231*1b3f573fSAndroid Build Coastguard Worker public void Merge(IMessage source, IMessage destination, FieldMask.MergeOptions options) 232*1b3f573fSAndroid Build Coastguard Worker { 233*1b3f573fSAndroid Build Coastguard Worker if (source.Descriptor != destination.Descriptor) 234*1b3f573fSAndroid Build Coastguard Worker { 235*1b3f573fSAndroid Build Coastguard Worker throw new InvalidProtocolBufferException("Cannot merge messages of different types."); 236*1b3f573fSAndroid Build Coastguard Worker } 237*1b3f573fSAndroid Build Coastguard Worker 238*1b3f573fSAndroid Build Coastguard Worker if (root.Children.Count == 0) 239*1b3f573fSAndroid Build Coastguard Worker { 240*1b3f573fSAndroid Build Coastguard Worker return; 241*1b3f573fSAndroid Build Coastguard Worker } 242*1b3f573fSAndroid Build Coastguard Worker 243*1b3f573fSAndroid Build Coastguard Worker Merge(root, "", source, destination, options); 244*1b3f573fSAndroid Build Coastguard Worker } 245*1b3f573fSAndroid Build Coastguard Worker 246*1b3f573fSAndroid Build Coastguard Worker /// <summary> 247*1b3f573fSAndroid Build Coastguard Worker /// Merges all fields specified by a sub-tree from <paramref name="source"/> to <paramref name="destination"/>. 248*1b3f573fSAndroid Build Coastguard Worker /// </summary> Merge( Node node, string path, IMessage source, IMessage destination, FieldMask.MergeOptions options)249*1b3f573fSAndroid Build Coastguard Worker private void Merge( 250*1b3f573fSAndroid Build Coastguard Worker Node node, 251*1b3f573fSAndroid Build Coastguard Worker string path, 252*1b3f573fSAndroid Build Coastguard Worker IMessage source, 253*1b3f573fSAndroid Build Coastguard Worker IMessage destination, 254*1b3f573fSAndroid Build Coastguard Worker FieldMask.MergeOptions options) 255*1b3f573fSAndroid Build Coastguard Worker { 256*1b3f573fSAndroid Build Coastguard Worker if (source.Descriptor != destination.Descriptor) 257*1b3f573fSAndroid Build Coastguard Worker { 258*1b3f573fSAndroid Build Coastguard Worker throw new InvalidProtocolBufferException($"source ({source.Descriptor}) and destination ({destination.Descriptor}) descriptor must be equal"); 259*1b3f573fSAndroid Build Coastguard Worker } 260*1b3f573fSAndroid Build Coastguard Worker 261*1b3f573fSAndroid Build Coastguard Worker var descriptor = source.Descriptor; 262*1b3f573fSAndroid Build Coastguard Worker foreach (var entry in node.Children) 263*1b3f573fSAndroid Build Coastguard Worker { 264*1b3f573fSAndroid Build Coastguard Worker var field = descriptor.FindFieldByName(entry.Key); 265*1b3f573fSAndroid Build Coastguard Worker if (field == null) 266*1b3f573fSAndroid Build Coastguard Worker { 267*1b3f573fSAndroid Build Coastguard Worker Debug.WriteLine($"Cannot find field \"{entry.Key}\" in message type \"{descriptor.FullName}\""); 268*1b3f573fSAndroid Build Coastguard Worker continue; 269*1b3f573fSAndroid Build Coastguard Worker } 270*1b3f573fSAndroid Build Coastguard Worker 271*1b3f573fSAndroid Build Coastguard Worker if (entry.Value.Children.Count != 0) 272*1b3f573fSAndroid Build Coastguard Worker { 273*1b3f573fSAndroid Build Coastguard Worker if (field.IsRepeated 274*1b3f573fSAndroid Build Coastguard Worker || field.FieldType != FieldType.Message) 275*1b3f573fSAndroid Build Coastguard Worker { 276*1b3f573fSAndroid Build Coastguard Worker Debug.WriteLine($"Field \"{field.FullName}\" is not a singular message field and cannot have sub-fields."); 277*1b3f573fSAndroid Build Coastguard Worker continue; 278*1b3f573fSAndroid Build Coastguard Worker } 279*1b3f573fSAndroid Build Coastguard Worker 280*1b3f573fSAndroid Build Coastguard Worker var sourceField = field.Accessor.GetValue(source); 281*1b3f573fSAndroid Build Coastguard Worker var destinationField = field.Accessor.GetValue(destination); 282*1b3f573fSAndroid Build Coastguard Worker if (sourceField == null 283*1b3f573fSAndroid Build Coastguard Worker && destinationField == null) 284*1b3f573fSAndroid Build Coastguard Worker { 285*1b3f573fSAndroid Build Coastguard Worker // If the message field is not present in both source and destination, skip recursing 286*1b3f573fSAndroid Build Coastguard Worker // so we don't create unnecessary empty messages. 287*1b3f573fSAndroid Build Coastguard Worker continue; 288*1b3f573fSAndroid Build Coastguard Worker } 289*1b3f573fSAndroid Build Coastguard Worker 290*1b3f573fSAndroid Build Coastguard Worker if (destinationField == null) 291*1b3f573fSAndroid Build Coastguard Worker { 292*1b3f573fSAndroid Build Coastguard Worker // If we have to merge but the destination does not contain the field, create it. 293*1b3f573fSAndroid Build Coastguard Worker destinationField = field.MessageType.Parser.CreateTemplate(); 294*1b3f573fSAndroid Build Coastguard Worker field.Accessor.SetValue(destination, destinationField); 295*1b3f573fSAndroid Build Coastguard Worker } 296*1b3f573fSAndroid Build Coastguard Worker 297*1b3f573fSAndroid Build Coastguard Worker var childPath = path.Length == 0 ? entry.Key : path + "." + entry.Key; 298*1b3f573fSAndroid Build Coastguard Worker Merge(entry.Value, childPath, (IMessage)sourceField, (IMessage)destinationField, options); 299*1b3f573fSAndroid Build Coastguard Worker continue; 300*1b3f573fSAndroid Build Coastguard Worker } 301*1b3f573fSAndroid Build Coastguard Worker 302*1b3f573fSAndroid Build Coastguard Worker if (field.IsRepeated) 303*1b3f573fSAndroid Build Coastguard Worker { 304*1b3f573fSAndroid Build Coastguard Worker if (options.ReplaceRepeatedFields) 305*1b3f573fSAndroid Build Coastguard Worker { 306*1b3f573fSAndroid Build Coastguard Worker field.Accessor.Clear(destination); 307*1b3f573fSAndroid Build Coastguard Worker } 308*1b3f573fSAndroid Build Coastguard Worker 309*1b3f573fSAndroid Build Coastguard Worker var sourceField = (IList)field.Accessor.GetValue(source); 310*1b3f573fSAndroid Build Coastguard Worker var destinationField = (IList)field.Accessor.GetValue(destination); 311*1b3f573fSAndroid Build Coastguard Worker foreach (var element in sourceField) 312*1b3f573fSAndroid Build Coastguard Worker { 313*1b3f573fSAndroid Build Coastguard Worker destinationField.Add(element); 314*1b3f573fSAndroid Build Coastguard Worker } 315*1b3f573fSAndroid Build Coastguard Worker } 316*1b3f573fSAndroid Build Coastguard Worker else 317*1b3f573fSAndroid Build Coastguard Worker { 318*1b3f573fSAndroid Build Coastguard Worker var sourceField = field.Accessor.GetValue(source); 319*1b3f573fSAndroid Build Coastguard Worker if (field.FieldType == FieldType.Message) 320*1b3f573fSAndroid Build Coastguard Worker { 321*1b3f573fSAndroid Build Coastguard Worker if (options.ReplaceMessageFields) 322*1b3f573fSAndroid Build Coastguard Worker { 323*1b3f573fSAndroid Build Coastguard Worker if (sourceField == null) 324*1b3f573fSAndroid Build Coastguard Worker { 325*1b3f573fSAndroid Build Coastguard Worker field.Accessor.Clear(destination); 326*1b3f573fSAndroid Build Coastguard Worker } 327*1b3f573fSAndroid Build Coastguard Worker else 328*1b3f573fSAndroid Build Coastguard Worker { 329*1b3f573fSAndroid Build Coastguard Worker field.Accessor.SetValue(destination, sourceField); 330*1b3f573fSAndroid Build Coastguard Worker } 331*1b3f573fSAndroid Build Coastguard Worker } 332*1b3f573fSAndroid Build Coastguard Worker else 333*1b3f573fSAndroid Build Coastguard Worker { 334*1b3f573fSAndroid Build Coastguard Worker if (sourceField != null) 335*1b3f573fSAndroid Build Coastguard Worker { 336*1b3f573fSAndroid Build Coastguard Worker var sourceByteString = ((IMessage)sourceField).ToByteString(); 337*1b3f573fSAndroid Build Coastguard Worker var destinationValue = (IMessage)field.Accessor.GetValue(destination); 338*1b3f573fSAndroid Build Coastguard Worker if (destinationValue != null) 339*1b3f573fSAndroid Build Coastguard Worker { 340*1b3f573fSAndroid Build Coastguard Worker destinationValue.MergeFrom(sourceByteString); 341*1b3f573fSAndroid Build Coastguard Worker } 342*1b3f573fSAndroid Build Coastguard Worker else 343*1b3f573fSAndroid Build Coastguard Worker { 344*1b3f573fSAndroid Build Coastguard Worker field.Accessor.SetValue(destination, field.MessageType.Parser.ParseFrom(sourceByteString)); 345*1b3f573fSAndroid Build Coastguard Worker } 346*1b3f573fSAndroid Build Coastguard Worker } 347*1b3f573fSAndroid Build Coastguard Worker } 348*1b3f573fSAndroid Build Coastguard Worker } 349*1b3f573fSAndroid Build Coastguard Worker else 350*1b3f573fSAndroid Build Coastguard Worker { 351*1b3f573fSAndroid Build Coastguard Worker if (sourceField != null 352*1b3f573fSAndroid Build Coastguard Worker || !options.ReplacePrimitiveFields) 353*1b3f573fSAndroid Build Coastguard Worker { 354*1b3f573fSAndroid Build Coastguard Worker field.Accessor.SetValue(destination, sourceField); 355*1b3f573fSAndroid Build Coastguard Worker } 356*1b3f573fSAndroid Build Coastguard Worker else 357*1b3f573fSAndroid Build Coastguard Worker { 358*1b3f573fSAndroid Build Coastguard Worker field.Accessor.Clear(destination); 359*1b3f573fSAndroid Build Coastguard Worker } 360*1b3f573fSAndroid Build Coastguard Worker } 361*1b3f573fSAndroid Build Coastguard Worker } 362*1b3f573fSAndroid Build Coastguard Worker } 363*1b3f573fSAndroid Build Coastguard Worker } 364*1b3f573fSAndroid Build Coastguard Worker } 365*1b3f573fSAndroid Build Coastguard Worker } 366