示例#1
0
        /// <summary>
        /// Merges all fields specified by this FieldMaskTree from <paramref name="source"/> to <paramref name="destination"/>.
        /// </summary>
        public void Merge(IMessage source, IMessage destination, FieldMask.MergeOptions options)
        {
            if (source.Descriptor != destination.Descriptor)
            {
                throw new InvalidProtocolBufferException("Cannot merge messages of different types.");
            }

            if (root.Children.Count == 0)
            {
                return;
            }

            Merge(root, "", source, destination, options);
        }
示例#2
0
        public void Merge(bool useDynamicMessage)
        {
            TestAllTypes value = new TestAllTypes
            {
                SingleInt32         = 1234,
                SingleNestedMessage = new TestAllTypes.Types.NestedMessage {
                    Bb = 5678
                },
                RepeatedInt32         = { 4321 },
                RepeatedNestedMessage = { new TestAllTypes.Types.NestedMessage {
                                              Bb = 8765
                                          } }
            };

            NestedTestAllTypes source = new NestedTestAllTypes
            {
                Payload = value,
                Child   = new NestedTestAllTypes {
                    Payload = value
                }
            };

            // Now we have a message source with the following structure:
            //   [root] -+- payload -+- single_int32
            //           |           +- single_nested_message
            //           |           +- repeated_int32
            //           |           +- repeated_nested_message
            //           |
            //           +- child --- payload -+- single_int32
            //                                 +- single_nested_message
            //                                 +- repeated_int32
            //                                 +- repeated_nested_message

            FieldMask.MergeOptions options = new FieldMask.MergeOptions();

            // Test merging each individual field.
            NestedTestAllTypes destination = new NestedTestAllTypes();

            Merge(new FieldMaskTree().AddFieldPath("payload.single_int32"),
                  source, destination, options, useDynamicMessage);
            NestedTestAllTypes expected = new NestedTestAllTypes
            {
                Payload = new TestAllTypes
                {
                    SingleInt32 = 1234
                }
            };

            Assert.AreEqual(expected, destination);

            destination = new NestedTestAllTypes();
            Merge(new FieldMaskTree().AddFieldPath("payload.single_nested_message"),
                  source, destination, options, useDynamicMessage);
            expected = new NestedTestAllTypes
            {
                Payload = new TestAllTypes
                {
                    SingleNestedMessage = new TestAllTypes.Types.NestedMessage {
                        Bb = 5678
                    }
                }
            };
            Assert.AreEqual(expected, destination);

            destination = new NestedTestAllTypes();
            Merge(new FieldMaskTree().AddFieldPath("payload.repeated_int32"),
                  source, destination, options, useDynamicMessage);
            expected = new NestedTestAllTypes
            {
                Payload = new TestAllTypes
                {
                    RepeatedInt32 = { 4321 }
                }
            };
            Assert.AreEqual(expected, destination);

            destination = new NestedTestAllTypes();
            Merge(new FieldMaskTree().AddFieldPath("payload.repeated_nested_message"),
                  source, destination, options, useDynamicMessage);
            expected = new NestedTestAllTypes
            {
                Payload = new TestAllTypes
                {
                    RepeatedNestedMessage = { new TestAllTypes.Types.NestedMessage {
                                                  Bb = 8765
                                              } }
                }
            };
            Assert.AreEqual(expected, destination);

            destination = new NestedTestAllTypes();
            Merge(
                new FieldMaskTree().AddFieldPath("child.payload.single_int32"),
                source,
                destination,
                options,
                useDynamicMessage);
            expected = new NestedTestAllTypes
            {
                Child = new NestedTestAllTypes
                {
                    Payload = new TestAllTypes
                    {
                        SingleInt32 = 1234
                    }
                }
            };
            Assert.AreEqual(expected, destination);

            destination = new NestedTestAllTypes();
            Merge(
                new FieldMaskTree().AddFieldPath("child.payload.single_nested_message"),
                source,
                destination,
                options,
                useDynamicMessage);
            expected = new NestedTestAllTypes
            {
                Child = new NestedTestAllTypes
                {
                    Payload = new TestAllTypes
                    {
                        SingleNestedMessage = new TestAllTypes.Types.NestedMessage {
                            Bb = 5678
                        }
                    }
                }
            };
            Assert.AreEqual(expected, destination);

            destination = new NestedTestAllTypes();
            Merge(new FieldMaskTree().AddFieldPath("child.payload.repeated_int32"),
                  source, destination, options, useDynamicMessage);
            expected = new NestedTestAllTypes
            {
                Child = new NestedTestAllTypes
                {
                    Payload = new TestAllTypes
                    {
                        RepeatedInt32 = { 4321 }
                    }
                }
            };
            Assert.AreEqual(expected, destination);

            destination = new NestedTestAllTypes();
            Merge(new FieldMaskTree().AddFieldPath("child.payload.repeated_nested_message"),
                  source, destination, options, useDynamicMessage);
            expected = new NestedTestAllTypes
            {
                Child = new NestedTestAllTypes
                {
                    Payload = new TestAllTypes
                    {
                        RepeatedNestedMessage = { new TestAllTypes.Types.NestedMessage {
                                                      Bb = 8765
                                                  } }
                    }
                }
            };
            Assert.AreEqual(expected, destination);

            destination = new NestedTestAllTypes();
            Merge(new FieldMaskTree().AddFieldPath("child").AddFieldPath("payload"),
                  source, destination, options, useDynamicMessage);
            Assert.AreEqual(source, destination);

            // Test repeated options.
            destination = new NestedTestAllTypes
            {
                Payload = new TestAllTypes
                {
                    RepeatedInt32 = { 1000 }
                }
            };
            Merge(new FieldMaskTree().AddFieldPath("payload.repeated_int32"),
                  source, destination, options, useDynamicMessage);
            // Default behavior is to append repeated fields.
            Assert.AreEqual(2, destination.Payload.RepeatedInt32.Count);
            Assert.AreEqual(1000, destination.Payload.RepeatedInt32[0]);
            Assert.AreEqual(4321, destination.Payload.RepeatedInt32[1]);
            // Change to replace repeated fields.
            options.ReplaceRepeatedFields = true;
            Merge(new FieldMaskTree().AddFieldPath("payload.repeated_int32"),
                  source, destination, options, useDynamicMessage);
            Assert.AreEqual(1, destination.Payload.RepeatedInt32.Count);
            Assert.AreEqual(4321, destination.Payload.RepeatedInt32[0]);

            // Test message options.
            destination = new NestedTestAllTypes
            {
                Payload = new TestAllTypes
                {
                    SingleInt32  = 1000,
                    SingleUint32 = 2000
                }
            };
            Merge(new FieldMaskTree().AddFieldPath("payload"),
                  source, destination, options, useDynamicMessage);
            // Default behavior is to merge message fields.
            Assert.AreEqual(1234, destination.Payload.SingleInt32);
            Assert.AreEqual(2000, destination.Payload.SingleUint32);

            // Test merging unset message fields.
            NestedTestAllTypes clearedSource = source.Clone();

            clearedSource.Payload = null;
            destination           = new NestedTestAllTypes();
            Merge(new FieldMaskTree().AddFieldPath("payload"),
                  clearedSource, destination, options, useDynamicMessage);
            Assert.IsNull(destination.Payload);

            // Skip a message field if they are unset in both source and target.
            destination = new NestedTestAllTypes();
            Merge(new FieldMaskTree().AddFieldPath("payload.single_int32"),
                  clearedSource, destination, options, useDynamicMessage);
            Assert.IsNull(destination.Payload);

            // Change to replace message fields.
            options.ReplaceMessageFields = true;
            destination = new NestedTestAllTypes
            {
                Payload = new TestAllTypes
                {
                    SingleInt32  = 1000,
                    SingleUint32 = 2000
                }
            };
            Merge(new FieldMaskTree().AddFieldPath("payload"),
                  source, destination, options, useDynamicMessage);
            Assert.AreEqual(1234, destination.Payload.SingleInt32);
            Assert.AreEqual(0, destination.Payload.SingleUint32);

            // Test merging unset message fields.
            destination = new NestedTestAllTypes
            {
                Payload = new TestAllTypes
                {
                    SingleInt32  = 1000,
                    SingleUint32 = 2000
                }
            };
            Merge(new FieldMaskTree().AddFieldPath("payload"),
                  clearedSource, destination, options, useDynamicMessage);
            Assert.IsNull(destination.Payload);

            // Test merging unset primitive fields.
            destination = source.Clone();
            destination.Payload.SingleInt32 = 0;
            NestedTestAllTypes sourceWithPayloadInt32Unset = destination;

            destination = source.Clone();
            Merge(new FieldMaskTree().AddFieldPath("payload.single_int32"),
                  sourceWithPayloadInt32Unset, destination, options, useDynamicMessage);
            Assert.AreEqual(0, destination.Payload.SingleInt32);

            // Change to clear unset primitive fields.
            options.ReplacePrimitiveFields = true;
            destination = source.Clone();
            Merge(new FieldMaskTree().AddFieldPath("payload.single_int32"),
                  sourceWithPayloadInt32Unset, destination, options, useDynamicMessage);
            Assert.IsNotNull(destination.Payload);
        }
示例#3
0
        private void Merge(FieldMaskTree tree, IMessage source, IMessage destination, FieldMask.MergeOptions options, bool useDynamicMessage)
        {
            if (useDynamicMessage)
            {
                var newSource = source.Descriptor.Parser.CreateTemplate();
                newSource.MergeFrom(source.ToByteString());

                var newDestination = source.Descriptor.Parser.CreateTemplate();
                newDestination.MergeFrom(destination.ToByteString());

                tree.Merge(newSource, newDestination, options);

                // Clear before merging:
                foreach (var fieldDescriptor in destination.Descriptor.Fields.InFieldNumberOrder())
                {
                    fieldDescriptor.Accessor.Clear(destination);
                }
                destination.MergeFrom(newDestination.ToByteString());
            }
            else
            {
                tree.Merge(source, destination, options);
            }
        }
示例#4
0
        /// <summary>
        /// Merges all fields specified by a sub-tree from <paramref name="source"/> to <paramref name="destination"/>.
        /// </summary>
        private void Merge(
            Node node,
            string path,
            IMessage source,
            IMessage destination,
            FieldMask.MergeOptions options)
        {
            if (source.Descriptor != destination.Descriptor)
            {
                throw new InvalidProtocolBufferException($"source ({source.Descriptor}) and destination ({destination.Descriptor}) descriptor must be equal");
            }

            var descriptor = source.Descriptor;

            foreach (var entry in node.Children)
            {
                var field = descriptor.FindFieldByName(entry.Key);
                if (field == null)
                {
                    Debug.WriteLine($"Cannot find field \"{entry.Key}\" in message type \"{descriptor.FullName}\"");
                    continue;
                }

                if (entry.Value.Children.Count != 0)
                {
                    if (field.IsRepeated ||
                        field.FieldType != FieldType.Message)
                    {
                        Debug.WriteLine($"Field \"{field.FullName}\" is not a singular message field and cannot have sub-fields.");
                        continue;
                    }

                    var sourceField      = field.Accessor.GetValue(source);
                    var destinationField = field.Accessor.GetValue(destination);
                    if (sourceField == null &&
                        destinationField == null)
                    {
                        // If the message field is not present in both source and destination, skip recursing
                        // so we don't create unnecessary empty messages.
                        continue;
                    }

                    if (destinationField == null)
                    {
                        // If we have to merge but the destination does not contain the field, create it.
                        destinationField = field.MessageType.Parser.CreateTemplate();
                        field.Accessor.SetValue(destination, destinationField);
                    }

                    var childPath = path.Length == 0 ? entry.Key : path + "." + entry.Key;
                    Merge(entry.Value, childPath, (IMessage)sourceField, (IMessage)destinationField, options);
                    continue;
                }

                if (field.IsRepeated)
                {
                    if (options.ReplaceRepeatedFields)
                    {
                        field.Accessor.Clear(destination);
                    }

                    var sourceField      = (IList)field.Accessor.GetValue(source);
                    var destinationField = (IList)field.Accessor.GetValue(destination);
                    foreach (var element in sourceField)
                    {
                        destinationField.Add(element);
                    }
                }
                else
                {
                    var sourceField = field.Accessor.GetValue(source);
                    if (field.FieldType == FieldType.Message)
                    {
                        if (options.ReplaceMessageFields)
                        {
                            if (sourceField == null)
                            {
                                field.Accessor.Clear(destination);
                            }
                            else
                            {
                                field.Accessor.SetValue(destination, sourceField);
                            }
                        }
                        else
                        {
                            if (sourceField != null)
                            {
                                // Well-known wrapper types are represented as nullable primitive types, so we do not "merge" them.
                                // Instead, any non-null value just overwrites the previous value directly.
                                if (field.MessageType.IsWrapperType)
                                {
                                    field.Accessor.SetValue(destination, sourceField);
                                }
                                else
                                {
                                    var sourceByteString = ((IMessage)sourceField).ToByteString();
                                    var destinationValue = (IMessage)field.Accessor.GetValue(destination);
                                    if (destinationValue != null)
                                    {
                                        destinationValue.MergeFrom(sourceByteString);
                                    }
                                    else
                                    {
                                        field.Accessor.SetValue(destination, field.MessageType.Parser.ParseFrom(sourceByteString));
                                    }
                                }
                            }
                        }
                    }
                    else
                    {
                        if (sourceField != null ||
                            !options.ReplacePrimitiveFields)
                        {
                            field.Accessor.SetValue(destination, sourceField);
                        }
                        else
                        {
                            field.Accessor.Clear(destination);
                        }
                    }
                }
            }
        }