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