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;
34ffe3c632Sopenharmony_ciusing System.Collections;
35ffe3c632Sopenharmony_ciusing System.Globalization;
36ffe3c632Sopenharmony_ciusing System.Text;
37ffe3c632Sopenharmony_ciusing Google.Protobuf.Reflection;
38ffe3c632Sopenharmony_ciusing Google.Protobuf.WellKnownTypes;
39ffe3c632Sopenharmony_ciusing System.IO;
40ffe3c632Sopenharmony_ciusing System.Linq;
41ffe3c632Sopenharmony_ciusing System.Collections.Generic;
42ffe3c632Sopenharmony_ciusing System.Reflection;
43ffe3c632Sopenharmony_ci
44ffe3c632Sopenharmony_cinamespace Google.Protobuf
45ffe3c632Sopenharmony_ci{
46ffe3c632Sopenharmony_ci    /// <summary>
47ffe3c632Sopenharmony_ci    /// Reflection-based converter from messages to JSON.
48ffe3c632Sopenharmony_ci    /// </summary>
49ffe3c632Sopenharmony_ci    /// <remarks>
50ffe3c632Sopenharmony_ci    /// <para>
51ffe3c632Sopenharmony_ci    /// Instances of this class are thread-safe, with no mutable state.
52ffe3c632Sopenharmony_ci    /// </para>
53ffe3c632Sopenharmony_ci    /// <para>
54ffe3c632Sopenharmony_ci    /// This is a simple start to get JSON formatting working. As it's reflection-based,
55ffe3c632Sopenharmony_ci    /// it's not as quick as baking calls into generated messages - but is a simpler implementation.
56ffe3c632Sopenharmony_ci    /// (This code is generally not heavily optimized.)
57ffe3c632Sopenharmony_ci    /// </para>
58ffe3c632Sopenharmony_ci    /// </remarks>
59ffe3c632Sopenharmony_ci    public sealed class JsonFormatter
60ffe3c632Sopenharmony_ci    {
61ffe3c632Sopenharmony_ci        internal const string AnyTypeUrlField = "@type";
62ffe3c632Sopenharmony_ci        internal const string AnyDiagnosticValueField = "@value";
63ffe3c632Sopenharmony_ci        internal const string AnyWellKnownTypeValueField = "value";
64ffe3c632Sopenharmony_ci        private const string TypeUrlPrefix = "type.googleapis.com";
65ffe3c632Sopenharmony_ci        private const string NameValueSeparator = ": ";
66ffe3c632Sopenharmony_ci        private const string PropertySeparator = ", ";
67ffe3c632Sopenharmony_ci
68ffe3c632Sopenharmony_ci        /// <summary>
69ffe3c632Sopenharmony_ci        /// Returns a formatter using the default settings.
70ffe3c632Sopenharmony_ci        /// </summary>
71ffe3c632Sopenharmony_ci        public static JsonFormatter Default { get; } = new JsonFormatter(Settings.Default);
72ffe3c632Sopenharmony_ci
73ffe3c632Sopenharmony_ci        // A JSON formatter which *only* exists
74ffe3c632Sopenharmony_ci        private static readonly JsonFormatter diagnosticFormatter = new JsonFormatter(Settings.Default);
75ffe3c632Sopenharmony_ci
76ffe3c632Sopenharmony_ci        /// <summary>
77ffe3c632Sopenharmony_ci        /// The JSON representation of the first 160 characters of Unicode.
78ffe3c632Sopenharmony_ci        /// Empty strings are replaced by the static constructor.
79ffe3c632Sopenharmony_ci        /// </summary>
80ffe3c632Sopenharmony_ci        private static readonly string[] CommonRepresentations = {
81ffe3c632Sopenharmony_ci            // C0 (ASCII and derivatives) control characters
82ffe3c632Sopenharmony_ci            "\\u0000", "\\u0001", "\\u0002", "\\u0003",  // 0x00
83ffe3c632Sopenharmony_ci          "\\u0004", "\\u0005", "\\u0006", "\\u0007",
84ffe3c632Sopenharmony_ci          "\\b",     "\\t",     "\\n",     "\\u000b",
85ffe3c632Sopenharmony_ci          "\\f",     "\\r",     "\\u000e", "\\u000f",
86ffe3c632Sopenharmony_ci          "\\u0010", "\\u0011", "\\u0012", "\\u0013",  // 0x10
87ffe3c632Sopenharmony_ci          "\\u0014", "\\u0015", "\\u0016", "\\u0017",
88ffe3c632Sopenharmony_ci          "\\u0018", "\\u0019", "\\u001a", "\\u001b",
89ffe3c632Sopenharmony_ci          "\\u001c", "\\u001d", "\\u001e", "\\u001f",
90ffe3c632Sopenharmony_ci            // Escaping of " and \ are required by www.json.org string definition.
91ffe3c632Sopenharmony_ci            // Escaping of < and > are required for HTML security.
92ffe3c632Sopenharmony_ci            "", "", "\\\"", "", "",        "", "",        "",  // 0x20
93ffe3c632Sopenharmony_ci          "", "", "",     "", "",        "", "",        "",
94ffe3c632Sopenharmony_ci          "", "", "",     "", "",        "", "",        "",  // 0x30
95ffe3c632Sopenharmony_ci          "", "", "",     "", "\\u003c", "", "\\u003e", "",
96ffe3c632Sopenharmony_ci          "", "", "",     "", "",        "", "",        "",  // 0x40
97ffe3c632Sopenharmony_ci          "", "", "",     "", "",        "", "",        "",
98ffe3c632Sopenharmony_ci          "", "", "",     "", "",        "", "",        "",  // 0x50
99ffe3c632Sopenharmony_ci          "", "", "",     "", "\\\\",    "", "",        "",
100ffe3c632Sopenharmony_ci          "", "", "",     "", "",        "", "",        "",  // 0x60
101ffe3c632Sopenharmony_ci          "", "", "",     "", "",        "", "",        "",
102ffe3c632Sopenharmony_ci          "", "", "",     "", "",        "", "",        "",  // 0x70
103ffe3c632Sopenharmony_ci          "", "", "",     "", "",        "", "",        "\\u007f",
104ffe3c632Sopenharmony_ci            // C1 (ISO 8859 and Unicode) extended control characters
105ffe3c632Sopenharmony_ci            "\\u0080", "\\u0081", "\\u0082", "\\u0083",  // 0x80
106ffe3c632Sopenharmony_ci          "\\u0084", "\\u0085", "\\u0086", "\\u0087",
107ffe3c632Sopenharmony_ci          "\\u0088", "\\u0089", "\\u008a", "\\u008b",
108ffe3c632Sopenharmony_ci          "\\u008c", "\\u008d", "\\u008e", "\\u008f",
109ffe3c632Sopenharmony_ci          "\\u0090", "\\u0091", "\\u0092", "\\u0093",  // 0x90
110ffe3c632Sopenharmony_ci          "\\u0094", "\\u0095", "\\u0096", "\\u0097",
111ffe3c632Sopenharmony_ci          "\\u0098", "\\u0099", "\\u009a", "\\u009b",
112ffe3c632Sopenharmony_ci          "\\u009c", "\\u009d", "\\u009e", "\\u009f"
113ffe3c632Sopenharmony_ci        };
114ffe3c632Sopenharmony_ci
115ffe3c632Sopenharmony_ci        static JsonFormatter()
116ffe3c632Sopenharmony_ci        {
117ffe3c632Sopenharmony_ci            for (int i = 0; i < CommonRepresentations.Length; i++)
118ffe3c632Sopenharmony_ci            {
119ffe3c632Sopenharmony_ci                if (CommonRepresentations[i] == "")
120ffe3c632Sopenharmony_ci                {
121ffe3c632Sopenharmony_ci                    CommonRepresentations[i] = ((char) i).ToString();
122ffe3c632Sopenharmony_ci                }
123ffe3c632Sopenharmony_ci            }
124ffe3c632Sopenharmony_ci        }
125ffe3c632Sopenharmony_ci
126ffe3c632Sopenharmony_ci        private readonly Settings settings;
127ffe3c632Sopenharmony_ci
128ffe3c632Sopenharmony_ci        private bool DiagnosticOnly => ReferenceEquals(this, diagnosticFormatter);
129ffe3c632Sopenharmony_ci
130ffe3c632Sopenharmony_ci        /// <summary>
131ffe3c632Sopenharmony_ci        /// Creates a new formatted with the given settings.
132ffe3c632Sopenharmony_ci        /// </summary>
133ffe3c632Sopenharmony_ci        /// <param name="settings">The settings.</param>
134ffe3c632Sopenharmony_ci        public JsonFormatter(Settings settings)
135ffe3c632Sopenharmony_ci        {
136ffe3c632Sopenharmony_ci            this.settings = ProtoPreconditions.CheckNotNull(settings, nameof(settings));
137ffe3c632Sopenharmony_ci        }
138ffe3c632Sopenharmony_ci
139ffe3c632Sopenharmony_ci        /// <summary>
140ffe3c632Sopenharmony_ci        /// Formats the specified message as JSON.
141ffe3c632Sopenharmony_ci        /// </summary>
142ffe3c632Sopenharmony_ci        /// <param name="message">The message to format.</param>
143ffe3c632Sopenharmony_ci        /// <returns>The formatted message.</returns>
144ffe3c632Sopenharmony_ci        public string Format(IMessage message)
145ffe3c632Sopenharmony_ci        {
146ffe3c632Sopenharmony_ci            var writer = new StringWriter();
147ffe3c632Sopenharmony_ci            Format(message, writer);
148ffe3c632Sopenharmony_ci            return writer.ToString();
149ffe3c632Sopenharmony_ci        }
150ffe3c632Sopenharmony_ci
151ffe3c632Sopenharmony_ci        /// <summary>
152ffe3c632Sopenharmony_ci        /// Formats the specified message as JSON.
153ffe3c632Sopenharmony_ci        /// </summary>
154ffe3c632Sopenharmony_ci        /// <param name="message">The message to format.</param>
155ffe3c632Sopenharmony_ci        /// <param name="writer">The TextWriter to write the formatted message to.</param>
156ffe3c632Sopenharmony_ci        /// <returns>The formatted message.</returns>
157ffe3c632Sopenharmony_ci        public void Format(IMessage message, TextWriter writer)
158ffe3c632Sopenharmony_ci        {
159ffe3c632Sopenharmony_ci            ProtoPreconditions.CheckNotNull(message, nameof(message));
160ffe3c632Sopenharmony_ci            ProtoPreconditions.CheckNotNull(writer, nameof(writer));
161ffe3c632Sopenharmony_ci
162ffe3c632Sopenharmony_ci            if (message.Descriptor.IsWellKnownType)
163ffe3c632Sopenharmony_ci            {
164ffe3c632Sopenharmony_ci                WriteWellKnownTypeValue(writer, message.Descriptor, message);
165ffe3c632Sopenharmony_ci            }
166ffe3c632Sopenharmony_ci            else
167ffe3c632Sopenharmony_ci            {
168ffe3c632Sopenharmony_ci                WriteMessage(writer, message);
169ffe3c632Sopenharmony_ci            }
170ffe3c632Sopenharmony_ci        }
171ffe3c632Sopenharmony_ci
172ffe3c632Sopenharmony_ci        /// <summary>
173ffe3c632Sopenharmony_ci        /// Converts a message to JSON for diagnostic purposes with no extra context.
174ffe3c632Sopenharmony_ci        /// </summary>
175ffe3c632Sopenharmony_ci        /// <remarks>
176ffe3c632Sopenharmony_ci        /// <para>
177ffe3c632Sopenharmony_ci        /// This differs from calling <see cref="Format(IMessage)"/> on the default JSON
178ffe3c632Sopenharmony_ci        /// formatter in its handling of <see cref="Any"/>. As no type registry is available
179ffe3c632Sopenharmony_ci        /// in <see cref="object.ToString"/> calls, the normal way of resolving the type of
180ffe3c632Sopenharmony_ci        /// an <c>Any</c> message cannot be applied. Instead, a JSON property named <c>@value</c>
181ffe3c632Sopenharmony_ci        /// is included with the base64 data from the <see cref="Any.Value"/> property of the message.
182ffe3c632Sopenharmony_ci        /// </para>
183ffe3c632Sopenharmony_ci        /// <para>The value returned by this method is only designed to be used for diagnostic
184ffe3c632Sopenharmony_ci        /// purposes. It may not be parsable by <see cref="JsonParser"/>, and may not be parsable
185ffe3c632Sopenharmony_ci        /// by other Protocol Buffer implementations.</para>
186ffe3c632Sopenharmony_ci        /// </remarks>
187ffe3c632Sopenharmony_ci        /// <param name="message">The message to format for diagnostic purposes.</param>
188ffe3c632Sopenharmony_ci        /// <returns>The diagnostic-only JSON representation of the message</returns>
189ffe3c632Sopenharmony_ci        public static string ToDiagnosticString(IMessage message)
190ffe3c632Sopenharmony_ci        {
191ffe3c632Sopenharmony_ci            ProtoPreconditions.CheckNotNull(message, nameof(message));
192ffe3c632Sopenharmony_ci            return diagnosticFormatter.Format(message);
193ffe3c632Sopenharmony_ci        }
194ffe3c632Sopenharmony_ci
195ffe3c632Sopenharmony_ci        private void WriteMessage(TextWriter writer, IMessage message)
196ffe3c632Sopenharmony_ci        {
197ffe3c632Sopenharmony_ci            if (message == null)
198ffe3c632Sopenharmony_ci            {
199ffe3c632Sopenharmony_ci                WriteNull(writer);
200ffe3c632Sopenharmony_ci                return;
201ffe3c632Sopenharmony_ci            }
202ffe3c632Sopenharmony_ci            if (DiagnosticOnly)
203ffe3c632Sopenharmony_ci            {
204ffe3c632Sopenharmony_ci                ICustomDiagnosticMessage customDiagnosticMessage = message as ICustomDiagnosticMessage;
205ffe3c632Sopenharmony_ci                if (customDiagnosticMessage != null)
206ffe3c632Sopenharmony_ci                {
207ffe3c632Sopenharmony_ci                    writer.Write(customDiagnosticMessage.ToDiagnosticString());
208ffe3c632Sopenharmony_ci                    return;
209ffe3c632Sopenharmony_ci                }
210ffe3c632Sopenharmony_ci            }
211ffe3c632Sopenharmony_ci            writer.Write("{ ");
212ffe3c632Sopenharmony_ci            bool writtenFields = WriteMessageFields(writer, message, false);
213ffe3c632Sopenharmony_ci            writer.Write(writtenFields ? " }" : "}");
214ffe3c632Sopenharmony_ci        }
215ffe3c632Sopenharmony_ci
216ffe3c632Sopenharmony_ci        private bool WriteMessageFields(TextWriter writer, IMessage message, bool assumeFirstFieldWritten)
217ffe3c632Sopenharmony_ci        {
218ffe3c632Sopenharmony_ci            var fields = message.Descriptor.Fields;
219ffe3c632Sopenharmony_ci            bool first = !assumeFirstFieldWritten;
220ffe3c632Sopenharmony_ci            // First non-oneof fields
221ffe3c632Sopenharmony_ci            foreach (var field in fields.InFieldNumberOrder())
222ffe3c632Sopenharmony_ci            {
223ffe3c632Sopenharmony_ci                var accessor = field.Accessor;
224ffe3c632Sopenharmony_ci                var value = accessor.GetValue(message);
225ffe3c632Sopenharmony_ci                if (!ShouldFormatFieldValue(message, field, value))
226ffe3c632Sopenharmony_ci                {
227ffe3c632Sopenharmony_ci                    continue;
228ffe3c632Sopenharmony_ci                }
229ffe3c632Sopenharmony_ci
230ffe3c632Sopenharmony_ci                if (!first)
231ffe3c632Sopenharmony_ci                {
232ffe3c632Sopenharmony_ci                    writer.Write(PropertySeparator);
233ffe3c632Sopenharmony_ci                }
234ffe3c632Sopenharmony_ci
235ffe3c632Sopenharmony_ci                WriteString(writer, accessor.Descriptor.JsonName);
236ffe3c632Sopenharmony_ci                writer.Write(NameValueSeparator);
237ffe3c632Sopenharmony_ci                WriteValue(writer, value);
238ffe3c632Sopenharmony_ci
239ffe3c632Sopenharmony_ci                first = false;
240ffe3c632Sopenharmony_ci            }
241ffe3c632Sopenharmony_ci            return !first;
242ffe3c632Sopenharmony_ci        }
243ffe3c632Sopenharmony_ci
244ffe3c632Sopenharmony_ci        /// <summary>
245ffe3c632Sopenharmony_ci        /// Determines whether or not a field value should be serialized according to the field,
246ffe3c632Sopenharmony_ci        /// its value in the message, and the settings of this formatter.
247ffe3c632Sopenharmony_ci        /// </summary>
248ffe3c632Sopenharmony_ci        private bool ShouldFormatFieldValue(IMessage message, FieldDescriptor field, object value) =>
249ffe3c632Sopenharmony_ci            field.HasPresence
250ffe3c632Sopenharmony_ci            // Fields that support presence *just* use that
251ffe3c632Sopenharmony_ci            ? field.Accessor.HasValue(message)
252ffe3c632Sopenharmony_ci            // Otherwise, format if either we've been asked to format default values, or if it's
253ffe3c632Sopenharmony_ci            // not a default value anyway.
254ffe3c632Sopenharmony_ci            : settings.FormatDefaultValues || !IsDefaultValue(field, value);
255ffe3c632Sopenharmony_ci
256ffe3c632Sopenharmony_ci        // Converted from java/core/src/main/java/com/google/protobuf/Descriptors.java
257ffe3c632Sopenharmony_ci        internal static string ToJsonName(string name)
258ffe3c632Sopenharmony_ci        {
259ffe3c632Sopenharmony_ci            StringBuilder result = new StringBuilder(name.Length);
260ffe3c632Sopenharmony_ci            bool isNextUpperCase = false;
261ffe3c632Sopenharmony_ci            foreach (char ch in name)
262ffe3c632Sopenharmony_ci            {
263ffe3c632Sopenharmony_ci                if (ch == '_')
264ffe3c632Sopenharmony_ci                {
265ffe3c632Sopenharmony_ci                    isNextUpperCase = true;
266ffe3c632Sopenharmony_ci                }
267ffe3c632Sopenharmony_ci                else if (isNextUpperCase)
268ffe3c632Sopenharmony_ci                {
269ffe3c632Sopenharmony_ci                    result.Append(char.ToUpperInvariant(ch));
270ffe3c632Sopenharmony_ci                    isNextUpperCase = false;
271ffe3c632Sopenharmony_ci                }
272ffe3c632Sopenharmony_ci                else
273ffe3c632Sopenharmony_ci                {
274ffe3c632Sopenharmony_ci                    result.Append(ch);
275ffe3c632Sopenharmony_ci                }
276ffe3c632Sopenharmony_ci            }
277ffe3c632Sopenharmony_ci            return result.ToString();
278ffe3c632Sopenharmony_ci        }
279ffe3c632Sopenharmony_ci
280ffe3c632Sopenharmony_ci        internal static string FromJsonName(string name)
281ffe3c632Sopenharmony_ci        {
282ffe3c632Sopenharmony_ci            StringBuilder result = new StringBuilder(name.Length);
283ffe3c632Sopenharmony_ci            foreach (char ch in name)
284ffe3c632Sopenharmony_ci            {
285ffe3c632Sopenharmony_ci                if (char.IsUpper(ch))
286ffe3c632Sopenharmony_ci                {
287ffe3c632Sopenharmony_ci                    result.Append('_');
288ffe3c632Sopenharmony_ci                    result.Append(char.ToLowerInvariant(ch));
289ffe3c632Sopenharmony_ci                }
290ffe3c632Sopenharmony_ci                else
291ffe3c632Sopenharmony_ci                {
292ffe3c632Sopenharmony_ci                    result.Append(ch);
293ffe3c632Sopenharmony_ci                }
294ffe3c632Sopenharmony_ci            }
295ffe3c632Sopenharmony_ci            return result.ToString();
296ffe3c632Sopenharmony_ci        }
297ffe3c632Sopenharmony_ci
298ffe3c632Sopenharmony_ci        private static void WriteNull(TextWriter writer)
299ffe3c632Sopenharmony_ci        {
300ffe3c632Sopenharmony_ci            writer.Write("null");
301ffe3c632Sopenharmony_ci        }
302ffe3c632Sopenharmony_ci
303ffe3c632Sopenharmony_ci        private static bool IsDefaultValue(FieldDescriptor descriptor, object value)
304ffe3c632Sopenharmony_ci        {
305ffe3c632Sopenharmony_ci            if (descriptor.IsMap)
306ffe3c632Sopenharmony_ci            {
307ffe3c632Sopenharmony_ci                IDictionary dictionary = (IDictionary) value;
308ffe3c632Sopenharmony_ci                return dictionary.Count == 0;
309ffe3c632Sopenharmony_ci            }
310ffe3c632Sopenharmony_ci            if (descriptor.IsRepeated)
311ffe3c632Sopenharmony_ci            {
312ffe3c632Sopenharmony_ci                IList list = (IList) value;
313ffe3c632Sopenharmony_ci                return list.Count == 0;
314ffe3c632Sopenharmony_ci            }
315ffe3c632Sopenharmony_ci            switch (descriptor.FieldType)
316ffe3c632Sopenharmony_ci            {
317ffe3c632Sopenharmony_ci                case FieldType.Bool:
318ffe3c632Sopenharmony_ci                    return (bool) value == false;
319ffe3c632Sopenharmony_ci                case FieldType.Bytes:
320ffe3c632Sopenharmony_ci                    return (ByteString) value == ByteString.Empty;
321ffe3c632Sopenharmony_ci                case FieldType.String:
322ffe3c632Sopenharmony_ci                    return (string) value == "";
323ffe3c632Sopenharmony_ci                case FieldType.Double:
324ffe3c632Sopenharmony_ci                    return (double) value == 0.0;
325ffe3c632Sopenharmony_ci                case FieldType.SInt32:
326ffe3c632Sopenharmony_ci                case FieldType.Int32:
327ffe3c632Sopenharmony_ci                case FieldType.SFixed32:
328ffe3c632Sopenharmony_ci                case FieldType.Enum:
329ffe3c632Sopenharmony_ci                    return (int) value == 0;
330ffe3c632Sopenharmony_ci                case FieldType.Fixed32:
331ffe3c632Sopenharmony_ci                case FieldType.UInt32:
332ffe3c632Sopenharmony_ci                    return (uint) value == 0;
333ffe3c632Sopenharmony_ci                case FieldType.Fixed64:
334ffe3c632Sopenharmony_ci                case FieldType.UInt64:
335ffe3c632Sopenharmony_ci                    return (ulong) value == 0;
336ffe3c632Sopenharmony_ci                case FieldType.SFixed64:
337ffe3c632Sopenharmony_ci                case FieldType.Int64:
338ffe3c632Sopenharmony_ci                case FieldType.SInt64:
339ffe3c632Sopenharmony_ci                    return (long) value == 0;
340ffe3c632Sopenharmony_ci                case FieldType.Float:
341ffe3c632Sopenharmony_ci                    return (float) value == 0f;
342ffe3c632Sopenharmony_ci                case FieldType.Message:
343ffe3c632Sopenharmony_ci                case FieldType.Group: // Never expect to get this, but...
344ffe3c632Sopenharmony_ci                    return value == null;
345ffe3c632Sopenharmony_ci                default:
346ffe3c632Sopenharmony_ci                    throw new ArgumentException("Invalid field type");
347ffe3c632Sopenharmony_ci            }
348ffe3c632Sopenharmony_ci        }
349ffe3c632Sopenharmony_ci
350ffe3c632Sopenharmony_ci        /// <summary>
351ffe3c632Sopenharmony_ci        /// Writes a single value to the given writer as JSON. Only types understood by
352ffe3c632Sopenharmony_ci        /// Protocol Buffers can be written in this way. This method is only exposed for
353ffe3c632Sopenharmony_ci        /// advanced use cases; most users should be using <see cref="Format(IMessage)"/>
354ffe3c632Sopenharmony_ci        /// or <see cref="Format(IMessage, TextWriter)"/>.
355ffe3c632Sopenharmony_ci        /// </summary>
356ffe3c632Sopenharmony_ci        /// <param name="writer">The writer to write the value to. Must not be null.</param>
357ffe3c632Sopenharmony_ci        /// <param name="value">The value to write. May be null.</param>
358ffe3c632Sopenharmony_ci        public void WriteValue(TextWriter writer, object value)
359ffe3c632Sopenharmony_ci        {
360ffe3c632Sopenharmony_ci            if (value == null || value is NullValue)
361ffe3c632Sopenharmony_ci            {
362ffe3c632Sopenharmony_ci                WriteNull(writer);
363ffe3c632Sopenharmony_ci            }
364ffe3c632Sopenharmony_ci            else if (value is bool)
365ffe3c632Sopenharmony_ci            {
366ffe3c632Sopenharmony_ci                writer.Write((bool)value ? "true" : "false");
367ffe3c632Sopenharmony_ci            }
368ffe3c632Sopenharmony_ci            else if (value is ByteString)
369ffe3c632Sopenharmony_ci            {
370ffe3c632Sopenharmony_ci                // Nothing in Base64 needs escaping
371ffe3c632Sopenharmony_ci                writer.Write('"');
372ffe3c632Sopenharmony_ci                writer.Write(((ByteString)value).ToBase64());
373ffe3c632Sopenharmony_ci                writer.Write('"');
374ffe3c632Sopenharmony_ci            }
375ffe3c632Sopenharmony_ci            else if (value is string)
376ffe3c632Sopenharmony_ci            {
377ffe3c632Sopenharmony_ci                WriteString(writer, (string)value);
378ffe3c632Sopenharmony_ci            }
379ffe3c632Sopenharmony_ci            else if (value is IDictionary)
380ffe3c632Sopenharmony_ci            {
381ffe3c632Sopenharmony_ci                WriteDictionary(writer, (IDictionary)value);
382ffe3c632Sopenharmony_ci            }
383ffe3c632Sopenharmony_ci            else if (value is IList)
384ffe3c632Sopenharmony_ci            {
385ffe3c632Sopenharmony_ci                WriteList(writer, (IList)value);
386ffe3c632Sopenharmony_ci            }
387ffe3c632Sopenharmony_ci            else if (value is int || value is uint)
388ffe3c632Sopenharmony_ci            {
389ffe3c632Sopenharmony_ci                IFormattable formattable = (IFormattable) value;
390ffe3c632Sopenharmony_ci                writer.Write(formattable.ToString("d", CultureInfo.InvariantCulture));
391ffe3c632Sopenharmony_ci            }
392ffe3c632Sopenharmony_ci            else if (value is long || value is ulong)
393ffe3c632Sopenharmony_ci            {
394ffe3c632Sopenharmony_ci                writer.Write('"');
395ffe3c632Sopenharmony_ci                IFormattable formattable = (IFormattable) value;
396ffe3c632Sopenharmony_ci                writer.Write(formattable.ToString("d", CultureInfo.InvariantCulture));
397ffe3c632Sopenharmony_ci                writer.Write('"');
398ffe3c632Sopenharmony_ci            }
399ffe3c632Sopenharmony_ci            else if (value is System.Enum)
400ffe3c632Sopenharmony_ci            {
401ffe3c632Sopenharmony_ci                if (settings.FormatEnumsAsIntegers)
402ffe3c632Sopenharmony_ci                {
403ffe3c632Sopenharmony_ci                    WriteValue(writer, (int)value);
404ffe3c632Sopenharmony_ci                }
405ffe3c632Sopenharmony_ci                else
406ffe3c632Sopenharmony_ci                {
407ffe3c632Sopenharmony_ci                    string name = OriginalEnumValueHelper.GetOriginalName(value);
408ffe3c632Sopenharmony_ci                    if (name != null)
409ffe3c632Sopenharmony_ci                    {
410ffe3c632Sopenharmony_ci                        WriteString(writer, name);
411ffe3c632Sopenharmony_ci                    }
412ffe3c632Sopenharmony_ci                    else
413ffe3c632Sopenharmony_ci                    {
414ffe3c632Sopenharmony_ci                        WriteValue(writer, (int)value);
415ffe3c632Sopenharmony_ci                    }
416ffe3c632Sopenharmony_ci                }
417ffe3c632Sopenharmony_ci            }
418ffe3c632Sopenharmony_ci            else if (value is float || value is double)
419ffe3c632Sopenharmony_ci            {
420ffe3c632Sopenharmony_ci                string text = ((IFormattable) value).ToString("r", CultureInfo.InvariantCulture);
421ffe3c632Sopenharmony_ci                if (text == "NaN" || text == "Infinity" || text == "-Infinity")
422ffe3c632Sopenharmony_ci                {
423ffe3c632Sopenharmony_ci                    writer.Write('"');
424ffe3c632Sopenharmony_ci                    writer.Write(text);
425ffe3c632Sopenharmony_ci                    writer.Write('"');
426ffe3c632Sopenharmony_ci                }
427ffe3c632Sopenharmony_ci                else
428ffe3c632Sopenharmony_ci                {
429ffe3c632Sopenharmony_ci                    writer.Write(text);
430ffe3c632Sopenharmony_ci                }
431ffe3c632Sopenharmony_ci            }
432ffe3c632Sopenharmony_ci            else if (value is IMessage)
433ffe3c632Sopenharmony_ci            {
434ffe3c632Sopenharmony_ci                Format((IMessage)value, writer);
435ffe3c632Sopenharmony_ci            }
436ffe3c632Sopenharmony_ci            else
437ffe3c632Sopenharmony_ci            {
438ffe3c632Sopenharmony_ci                throw new ArgumentException("Unable to format value of type " + value.GetType());
439ffe3c632Sopenharmony_ci            }
440ffe3c632Sopenharmony_ci        }
441ffe3c632Sopenharmony_ci
442ffe3c632Sopenharmony_ci        /// <summary>
443ffe3c632Sopenharmony_ci        /// Central interception point for well-known type formatting. Any well-known types which
444ffe3c632Sopenharmony_ci        /// don't need special handling can fall back to WriteMessage. We avoid assuming that the
445ffe3c632Sopenharmony_ci        /// values are using the embedded well-known types, in order to allow for dynamic messages
446ffe3c632Sopenharmony_ci        /// in the future.
447ffe3c632Sopenharmony_ci        /// </summary>
448ffe3c632Sopenharmony_ci        private void WriteWellKnownTypeValue(TextWriter writer, MessageDescriptor descriptor, object value)
449ffe3c632Sopenharmony_ci        {
450ffe3c632Sopenharmony_ci            // Currently, we can never actually get here, because null values are always handled by the caller. But if we *could*,
451ffe3c632Sopenharmony_ci            // this would do the right thing.
452ffe3c632Sopenharmony_ci            if (value == null)
453ffe3c632Sopenharmony_ci            {
454ffe3c632Sopenharmony_ci                WriteNull(writer);
455ffe3c632Sopenharmony_ci                return;
456ffe3c632Sopenharmony_ci            }
457ffe3c632Sopenharmony_ci            // For wrapper types, the value will either be the (possibly boxed) "native" value,
458ffe3c632Sopenharmony_ci            // or the message itself if we're formatting it at the top level (e.g. just calling ToString on the object itself).
459ffe3c632Sopenharmony_ci            // If it's the message form, we can extract the value first, which *will* be the (possibly boxed) native value,
460ffe3c632Sopenharmony_ci            // and then proceed, writing it as if we were definitely in a field. (We never need to wrap it in an extra string...
461ffe3c632Sopenharmony_ci            // WriteValue will do the right thing.)
462ffe3c632Sopenharmony_ci            if (descriptor.IsWrapperType)
463ffe3c632Sopenharmony_ci            {
464ffe3c632Sopenharmony_ci                if (value is IMessage)
465ffe3c632Sopenharmony_ci                {
466ffe3c632Sopenharmony_ci                    var message = (IMessage) value;
467ffe3c632Sopenharmony_ci                    value = message.Descriptor.Fields[WrappersReflection.WrapperValueFieldNumber].Accessor.GetValue(message);
468ffe3c632Sopenharmony_ci                }
469ffe3c632Sopenharmony_ci                WriteValue(writer, value);
470ffe3c632Sopenharmony_ci                return;
471ffe3c632Sopenharmony_ci            }
472ffe3c632Sopenharmony_ci            if (descriptor.FullName == Timestamp.Descriptor.FullName)
473ffe3c632Sopenharmony_ci            {
474ffe3c632Sopenharmony_ci                WriteTimestamp(writer, (IMessage)value);
475ffe3c632Sopenharmony_ci                return;
476ffe3c632Sopenharmony_ci            }
477ffe3c632Sopenharmony_ci            if (descriptor.FullName == Duration.Descriptor.FullName)
478ffe3c632Sopenharmony_ci            {
479ffe3c632Sopenharmony_ci                WriteDuration(writer, (IMessage)value);
480ffe3c632Sopenharmony_ci                return;
481ffe3c632Sopenharmony_ci            }
482ffe3c632Sopenharmony_ci            if (descriptor.FullName == FieldMask.Descriptor.FullName)
483ffe3c632Sopenharmony_ci            {
484ffe3c632Sopenharmony_ci                WriteFieldMask(writer, (IMessage)value);
485ffe3c632Sopenharmony_ci                return;
486ffe3c632Sopenharmony_ci            }
487ffe3c632Sopenharmony_ci            if (descriptor.FullName == Struct.Descriptor.FullName)
488ffe3c632Sopenharmony_ci            {
489ffe3c632Sopenharmony_ci                WriteStruct(writer, (IMessage)value);
490ffe3c632Sopenharmony_ci                return;
491ffe3c632Sopenharmony_ci            }
492ffe3c632Sopenharmony_ci            if (descriptor.FullName == ListValue.Descriptor.FullName)
493ffe3c632Sopenharmony_ci            {
494ffe3c632Sopenharmony_ci                var fieldAccessor = descriptor.Fields[ListValue.ValuesFieldNumber].Accessor;
495ffe3c632Sopenharmony_ci                WriteList(writer, (IList)fieldAccessor.GetValue((IMessage)value));
496ffe3c632Sopenharmony_ci                return;
497ffe3c632Sopenharmony_ci            }
498ffe3c632Sopenharmony_ci            if (descriptor.FullName == Value.Descriptor.FullName)
499ffe3c632Sopenharmony_ci            {
500ffe3c632Sopenharmony_ci                WriteStructFieldValue(writer, (IMessage)value);
501ffe3c632Sopenharmony_ci                return;
502ffe3c632Sopenharmony_ci            }
503ffe3c632Sopenharmony_ci            if (descriptor.FullName == Any.Descriptor.FullName)
504ffe3c632Sopenharmony_ci            {
505ffe3c632Sopenharmony_ci                WriteAny(writer, (IMessage)value);
506ffe3c632Sopenharmony_ci                return;
507ffe3c632Sopenharmony_ci            }
508ffe3c632Sopenharmony_ci            WriteMessage(writer, (IMessage)value);
509ffe3c632Sopenharmony_ci        }
510ffe3c632Sopenharmony_ci
511ffe3c632Sopenharmony_ci        private void WriteTimestamp(TextWriter writer, IMessage value)
512ffe3c632Sopenharmony_ci        {
513ffe3c632Sopenharmony_ci            // TODO: In the common case where this *is* using the built-in Timestamp type, we could
514ffe3c632Sopenharmony_ci            // avoid all the reflection at this point, by casting to Timestamp. In the interests of
515ffe3c632Sopenharmony_ci            // avoiding subtle bugs, don't do that until we've implemented DynamicMessage so that we can prove
516ffe3c632Sopenharmony_ci            // it still works in that case.
517ffe3c632Sopenharmony_ci            int nanos = (int) value.Descriptor.Fields[Timestamp.NanosFieldNumber].Accessor.GetValue(value);
518ffe3c632Sopenharmony_ci            long seconds = (long) value.Descriptor.Fields[Timestamp.SecondsFieldNumber].Accessor.GetValue(value);
519ffe3c632Sopenharmony_ci            writer.Write(Timestamp.ToJson(seconds, nanos, DiagnosticOnly));
520ffe3c632Sopenharmony_ci        }
521ffe3c632Sopenharmony_ci
522ffe3c632Sopenharmony_ci        private void WriteDuration(TextWriter writer, IMessage value)
523ffe3c632Sopenharmony_ci        {
524ffe3c632Sopenharmony_ci            // TODO: Same as for WriteTimestamp
525ffe3c632Sopenharmony_ci            int nanos = (int) value.Descriptor.Fields[Duration.NanosFieldNumber].Accessor.GetValue(value);
526ffe3c632Sopenharmony_ci            long seconds = (long) value.Descriptor.Fields[Duration.SecondsFieldNumber].Accessor.GetValue(value);
527ffe3c632Sopenharmony_ci            writer.Write(Duration.ToJson(seconds, nanos, DiagnosticOnly));
528ffe3c632Sopenharmony_ci        }
529ffe3c632Sopenharmony_ci
530ffe3c632Sopenharmony_ci        private void WriteFieldMask(TextWriter writer, IMessage value)
531ffe3c632Sopenharmony_ci        {
532ffe3c632Sopenharmony_ci            var paths = (IList<string>) value.Descriptor.Fields[FieldMask.PathsFieldNumber].Accessor.GetValue(value);
533ffe3c632Sopenharmony_ci            writer.Write(FieldMask.ToJson(paths, DiagnosticOnly));
534ffe3c632Sopenharmony_ci        }
535ffe3c632Sopenharmony_ci
536ffe3c632Sopenharmony_ci        private void WriteAny(TextWriter writer, IMessage value)
537ffe3c632Sopenharmony_ci        {
538ffe3c632Sopenharmony_ci            if (DiagnosticOnly)
539ffe3c632Sopenharmony_ci            {
540ffe3c632Sopenharmony_ci                WriteDiagnosticOnlyAny(writer, value);
541ffe3c632Sopenharmony_ci                return;
542ffe3c632Sopenharmony_ci            }
543ffe3c632Sopenharmony_ci
544ffe3c632Sopenharmony_ci            string typeUrl = (string) value.Descriptor.Fields[Any.TypeUrlFieldNumber].Accessor.GetValue(value);
545ffe3c632Sopenharmony_ci            ByteString data = (ByteString) value.Descriptor.Fields[Any.ValueFieldNumber].Accessor.GetValue(value);
546ffe3c632Sopenharmony_ci            string typeName = Any.GetTypeName(typeUrl);
547ffe3c632Sopenharmony_ci            MessageDescriptor descriptor = settings.TypeRegistry.Find(typeName);
548ffe3c632Sopenharmony_ci            if (descriptor == null)
549ffe3c632Sopenharmony_ci            {
550ffe3c632Sopenharmony_ci                throw new InvalidOperationException($"Type registry has no descriptor for type name '{typeName}'");
551ffe3c632Sopenharmony_ci            }
552ffe3c632Sopenharmony_ci            IMessage message = descriptor.Parser.ParseFrom(data);
553ffe3c632Sopenharmony_ci            writer.Write("{ ");
554ffe3c632Sopenharmony_ci            WriteString(writer, AnyTypeUrlField);
555ffe3c632Sopenharmony_ci            writer.Write(NameValueSeparator);
556ffe3c632Sopenharmony_ci            WriteString(writer, typeUrl);
557ffe3c632Sopenharmony_ci
558ffe3c632Sopenharmony_ci            if (descriptor.IsWellKnownType)
559ffe3c632Sopenharmony_ci            {
560ffe3c632Sopenharmony_ci                writer.Write(PropertySeparator);
561ffe3c632Sopenharmony_ci                WriteString(writer, AnyWellKnownTypeValueField);
562ffe3c632Sopenharmony_ci                writer.Write(NameValueSeparator);
563ffe3c632Sopenharmony_ci                WriteWellKnownTypeValue(writer, descriptor, message);
564ffe3c632Sopenharmony_ci            }
565ffe3c632Sopenharmony_ci            else
566ffe3c632Sopenharmony_ci            {
567ffe3c632Sopenharmony_ci                WriteMessageFields(writer, message, true);
568ffe3c632Sopenharmony_ci            }
569ffe3c632Sopenharmony_ci            writer.Write(" }");
570ffe3c632Sopenharmony_ci        }
571ffe3c632Sopenharmony_ci
572ffe3c632Sopenharmony_ci        private void WriteDiagnosticOnlyAny(TextWriter writer, IMessage value)
573ffe3c632Sopenharmony_ci        {
574ffe3c632Sopenharmony_ci            string typeUrl = (string) value.Descriptor.Fields[Any.TypeUrlFieldNumber].Accessor.GetValue(value);
575ffe3c632Sopenharmony_ci            ByteString data = (ByteString) value.Descriptor.Fields[Any.ValueFieldNumber].Accessor.GetValue(value);
576ffe3c632Sopenharmony_ci            writer.Write("{ ");
577ffe3c632Sopenharmony_ci            WriteString(writer, AnyTypeUrlField);
578ffe3c632Sopenharmony_ci            writer.Write(NameValueSeparator);
579ffe3c632Sopenharmony_ci            WriteString(writer, typeUrl);
580ffe3c632Sopenharmony_ci            writer.Write(PropertySeparator);
581ffe3c632Sopenharmony_ci            WriteString(writer, AnyDiagnosticValueField);
582ffe3c632Sopenharmony_ci            writer.Write(NameValueSeparator);
583ffe3c632Sopenharmony_ci            writer.Write('"');
584ffe3c632Sopenharmony_ci            writer.Write(data.ToBase64());
585ffe3c632Sopenharmony_ci            writer.Write('"');
586ffe3c632Sopenharmony_ci            writer.Write(" }");
587ffe3c632Sopenharmony_ci        }
588ffe3c632Sopenharmony_ci
589ffe3c632Sopenharmony_ci        private void WriteStruct(TextWriter writer, IMessage message)
590ffe3c632Sopenharmony_ci        {
591ffe3c632Sopenharmony_ci            writer.Write("{ ");
592ffe3c632Sopenharmony_ci            IDictionary fields = (IDictionary) message.Descriptor.Fields[Struct.FieldsFieldNumber].Accessor.GetValue(message);
593ffe3c632Sopenharmony_ci            bool first = true;
594ffe3c632Sopenharmony_ci            foreach (DictionaryEntry entry in fields)
595ffe3c632Sopenharmony_ci            {
596ffe3c632Sopenharmony_ci                string key = (string) entry.Key;
597ffe3c632Sopenharmony_ci                IMessage value = (IMessage) entry.Value;
598ffe3c632Sopenharmony_ci                if (string.IsNullOrEmpty(key) || value == null)
599ffe3c632Sopenharmony_ci                {
600ffe3c632Sopenharmony_ci                    throw new InvalidOperationException("Struct fields cannot have an empty key or a null value.");
601ffe3c632Sopenharmony_ci                }
602ffe3c632Sopenharmony_ci
603ffe3c632Sopenharmony_ci                if (!first)
604ffe3c632Sopenharmony_ci                {
605ffe3c632Sopenharmony_ci                    writer.Write(PropertySeparator);
606ffe3c632Sopenharmony_ci                }
607ffe3c632Sopenharmony_ci                WriteString(writer, key);
608ffe3c632Sopenharmony_ci                writer.Write(NameValueSeparator);
609ffe3c632Sopenharmony_ci                WriteStructFieldValue(writer, value);
610ffe3c632Sopenharmony_ci                first = false;
611ffe3c632Sopenharmony_ci            }
612ffe3c632Sopenharmony_ci            writer.Write(first ? "}" : " }");
613ffe3c632Sopenharmony_ci        }
614ffe3c632Sopenharmony_ci
615ffe3c632Sopenharmony_ci        private void WriteStructFieldValue(TextWriter writer, IMessage message)
616ffe3c632Sopenharmony_ci        {
617ffe3c632Sopenharmony_ci            var specifiedField = message.Descriptor.Oneofs[0].Accessor.GetCaseFieldDescriptor(message);
618ffe3c632Sopenharmony_ci            if (specifiedField == null)
619ffe3c632Sopenharmony_ci            {
620ffe3c632Sopenharmony_ci                throw new InvalidOperationException("Value message must contain a value for the oneof.");
621ffe3c632Sopenharmony_ci            }
622ffe3c632Sopenharmony_ci
623ffe3c632Sopenharmony_ci            object value = specifiedField.Accessor.GetValue(message);
624ffe3c632Sopenharmony_ci
625ffe3c632Sopenharmony_ci            switch (specifiedField.FieldNumber)
626ffe3c632Sopenharmony_ci            {
627ffe3c632Sopenharmony_ci                case Value.BoolValueFieldNumber:
628ffe3c632Sopenharmony_ci                case Value.StringValueFieldNumber:
629ffe3c632Sopenharmony_ci                case Value.NumberValueFieldNumber:
630ffe3c632Sopenharmony_ci                    WriteValue(writer, value);
631ffe3c632Sopenharmony_ci                    return;
632ffe3c632Sopenharmony_ci                case Value.StructValueFieldNumber:
633ffe3c632Sopenharmony_ci                case Value.ListValueFieldNumber:
634ffe3c632Sopenharmony_ci                    // Structs and ListValues are nested messages, and already well-known types.
635ffe3c632Sopenharmony_ci                    var nestedMessage = (IMessage) specifiedField.Accessor.GetValue(message);
636ffe3c632Sopenharmony_ci                    WriteWellKnownTypeValue(writer, nestedMessage.Descriptor, nestedMessage);
637ffe3c632Sopenharmony_ci                    return;
638ffe3c632Sopenharmony_ci                case Value.NullValueFieldNumber:
639ffe3c632Sopenharmony_ci                    WriteNull(writer);
640ffe3c632Sopenharmony_ci                    return;
641ffe3c632Sopenharmony_ci                default:
642ffe3c632Sopenharmony_ci                    throw new InvalidOperationException("Unexpected case in struct field: " + specifiedField.FieldNumber);
643ffe3c632Sopenharmony_ci            }
644ffe3c632Sopenharmony_ci        }
645ffe3c632Sopenharmony_ci
646ffe3c632Sopenharmony_ci        internal void WriteList(TextWriter writer, IList list)
647ffe3c632Sopenharmony_ci        {
648ffe3c632Sopenharmony_ci            writer.Write("[ ");
649ffe3c632Sopenharmony_ci            bool first = true;
650ffe3c632Sopenharmony_ci            foreach (var value in list)
651ffe3c632Sopenharmony_ci            {
652ffe3c632Sopenharmony_ci                if (!first)
653ffe3c632Sopenharmony_ci                {
654ffe3c632Sopenharmony_ci                    writer.Write(PropertySeparator);
655ffe3c632Sopenharmony_ci                }
656ffe3c632Sopenharmony_ci                WriteValue(writer, value);
657ffe3c632Sopenharmony_ci                first = false;
658ffe3c632Sopenharmony_ci            }
659ffe3c632Sopenharmony_ci            writer.Write(first ? "]" : " ]");
660ffe3c632Sopenharmony_ci        }
661ffe3c632Sopenharmony_ci
662ffe3c632Sopenharmony_ci        internal void WriteDictionary(TextWriter writer, IDictionary dictionary)
663ffe3c632Sopenharmony_ci        {
664ffe3c632Sopenharmony_ci            writer.Write("{ ");
665ffe3c632Sopenharmony_ci            bool first = true;
666ffe3c632Sopenharmony_ci            // This will box each pair. Could use IDictionaryEnumerator, but that's ugly in terms of disposal.
667ffe3c632Sopenharmony_ci            foreach (DictionaryEntry pair in dictionary)
668ffe3c632Sopenharmony_ci            {
669ffe3c632Sopenharmony_ci                if (!first)
670ffe3c632Sopenharmony_ci                {
671ffe3c632Sopenharmony_ci                    writer.Write(PropertySeparator);
672ffe3c632Sopenharmony_ci                }
673ffe3c632Sopenharmony_ci                string keyText;
674ffe3c632Sopenharmony_ci                if (pair.Key is string)
675ffe3c632Sopenharmony_ci                {
676ffe3c632Sopenharmony_ci                    keyText = (string) pair.Key;
677ffe3c632Sopenharmony_ci                }
678ffe3c632Sopenharmony_ci                else if (pair.Key is bool)
679ffe3c632Sopenharmony_ci                {
680ffe3c632Sopenharmony_ci                    keyText = (bool) pair.Key ? "true" : "false";
681ffe3c632Sopenharmony_ci                }
682ffe3c632Sopenharmony_ci                else if (pair.Key is int || pair.Key is uint | pair.Key is long || pair.Key is ulong)
683ffe3c632Sopenharmony_ci                {
684ffe3c632Sopenharmony_ci                    keyText = ((IFormattable) pair.Key).ToString("d", CultureInfo.InvariantCulture);
685ffe3c632Sopenharmony_ci                }
686ffe3c632Sopenharmony_ci                else
687ffe3c632Sopenharmony_ci                {
688ffe3c632Sopenharmony_ci                    if (pair.Key == null)
689ffe3c632Sopenharmony_ci                    {
690ffe3c632Sopenharmony_ci                        throw new ArgumentException("Dictionary has entry with null key");
691ffe3c632Sopenharmony_ci                    }
692ffe3c632Sopenharmony_ci                    throw new ArgumentException("Unhandled dictionary key type: " + pair.Key.GetType());
693ffe3c632Sopenharmony_ci                }
694ffe3c632Sopenharmony_ci                WriteString(writer, keyText);
695ffe3c632Sopenharmony_ci                writer.Write(NameValueSeparator);
696ffe3c632Sopenharmony_ci                WriteValue(writer, pair.Value);
697ffe3c632Sopenharmony_ci                first = false;
698ffe3c632Sopenharmony_ci            }
699ffe3c632Sopenharmony_ci            writer.Write(first ? "}" : " }");
700ffe3c632Sopenharmony_ci        }
701ffe3c632Sopenharmony_ci
702ffe3c632Sopenharmony_ci        /// <summary>
703ffe3c632Sopenharmony_ci        /// Writes a string (including leading and trailing double quotes) to a builder, escaping as required.
704ffe3c632Sopenharmony_ci        /// </summary>
705ffe3c632Sopenharmony_ci        /// <remarks>
706ffe3c632Sopenharmony_ci        /// Other than surrogate pair handling, this code is mostly taken from src/google/protobuf/util/internal/json_escaping.cc.
707ffe3c632Sopenharmony_ci        /// </remarks>
708ffe3c632Sopenharmony_ci        internal static void WriteString(TextWriter writer, string text)
709ffe3c632Sopenharmony_ci        {
710ffe3c632Sopenharmony_ci            writer.Write('"');
711ffe3c632Sopenharmony_ci            for (int i = 0; i < text.Length; i++)
712ffe3c632Sopenharmony_ci            {
713ffe3c632Sopenharmony_ci                char c = text[i];
714ffe3c632Sopenharmony_ci                if (c < 0xa0)
715ffe3c632Sopenharmony_ci                {
716ffe3c632Sopenharmony_ci                    writer.Write(CommonRepresentations[c]);
717ffe3c632Sopenharmony_ci                    continue;
718ffe3c632Sopenharmony_ci                }
719ffe3c632Sopenharmony_ci                if (char.IsHighSurrogate(c))
720ffe3c632Sopenharmony_ci                {
721ffe3c632Sopenharmony_ci                    // Encountered first part of a surrogate pair.
722ffe3c632Sopenharmony_ci                    // Check that we have the whole pair, and encode both parts as hex.
723ffe3c632Sopenharmony_ci                    i++;
724ffe3c632Sopenharmony_ci                    if (i == text.Length || !char.IsLowSurrogate(text[i]))
725ffe3c632Sopenharmony_ci                    {
726ffe3c632Sopenharmony_ci                        throw new ArgumentException("String contains low surrogate not followed by high surrogate");
727ffe3c632Sopenharmony_ci                    }
728ffe3c632Sopenharmony_ci                    HexEncodeUtf16CodeUnit(writer, c);
729ffe3c632Sopenharmony_ci                    HexEncodeUtf16CodeUnit(writer, text[i]);
730ffe3c632Sopenharmony_ci                    continue;
731ffe3c632Sopenharmony_ci                }
732ffe3c632Sopenharmony_ci                else if (char.IsLowSurrogate(c))
733ffe3c632Sopenharmony_ci                {
734ffe3c632Sopenharmony_ci                    throw new ArgumentException("String contains high surrogate not preceded by low surrogate");
735ffe3c632Sopenharmony_ci                }
736ffe3c632Sopenharmony_ci                switch ((uint) c)
737ffe3c632Sopenharmony_ci                {
738ffe3c632Sopenharmony_ci                    // These are not required by json spec
739ffe3c632Sopenharmony_ci                    // but used to prevent security bugs in javascript.
740ffe3c632Sopenharmony_ci                    case 0xfeff:  // Zero width no-break space
741ffe3c632Sopenharmony_ci                    case 0xfff9:  // Interlinear annotation anchor
742ffe3c632Sopenharmony_ci                    case 0xfffa:  // Interlinear annotation separator
743ffe3c632Sopenharmony_ci                    case 0xfffb:  // Interlinear annotation terminator
744ffe3c632Sopenharmony_ci
745ffe3c632Sopenharmony_ci                    case 0x00ad:  // Soft-hyphen
746ffe3c632Sopenharmony_ci                    case 0x06dd:  // Arabic end of ayah
747ffe3c632Sopenharmony_ci                    case 0x070f:  // Syriac abbreviation mark
748ffe3c632Sopenharmony_ci                    case 0x17b4:  // Khmer vowel inherent Aq
749ffe3c632Sopenharmony_ci                    case 0x17b5:  // Khmer vowel inherent Aa
750ffe3c632Sopenharmony_ci                        HexEncodeUtf16CodeUnit(writer, c);
751ffe3c632Sopenharmony_ci                        break;
752ffe3c632Sopenharmony_ci
753ffe3c632Sopenharmony_ci                    default:
754ffe3c632Sopenharmony_ci                        if ((c >= 0x0600 && c <= 0x0603) ||  // Arabic signs
755ffe3c632Sopenharmony_ci                            (c >= 0x200b && c <= 0x200f) ||  // Zero width etc.
756ffe3c632Sopenharmony_ci                            (c >= 0x2028 && c <= 0x202e) ||  // Separators etc.
757ffe3c632Sopenharmony_ci                            (c >= 0x2060 && c <= 0x2064) ||  // Invisible etc.
758ffe3c632Sopenharmony_ci                            (c >= 0x206a && c <= 0x206f))
759ffe3c632Sopenharmony_ci                        {
760ffe3c632Sopenharmony_ci                            HexEncodeUtf16CodeUnit(writer, c);
761ffe3c632Sopenharmony_ci                        }
762ffe3c632Sopenharmony_ci                        else
763ffe3c632Sopenharmony_ci                        {
764ffe3c632Sopenharmony_ci                            // No handling of surrogates here - that's done earlier
765ffe3c632Sopenharmony_ci                            writer.Write(c);
766ffe3c632Sopenharmony_ci                        }
767ffe3c632Sopenharmony_ci                        break;
768ffe3c632Sopenharmony_ci                }
769ffe3c632Sopenharmony_ci            }
770ffe3c632Sopenharmony_ci            writer.Write('"');
771ffe3c632Sopenharmony_ci        }
772ffe3c632Sopenharmony_ci
773ffe3c632Sopenharmony_ci        private const string Hex = "0123456789abcdef";
774ffe3c632Sopenharmony_ci        private static void HexEncodeUtf16CodeUnit(TextWriter writer, char c)
775ffe3c632Sopenharmony_ci        {
776ffe3c632Sopenharmony_ci            writer.Write("\\u");
777ffe3c632Sopenharmony_ci            writer.Write(Hex[(c >> 12) & 0xf]);
778ffe3c632Sopenharmony_ci            writer.Write(Hex[(c >> 8) & 0xf]);
779ffe3c632Sopenharmony_ci            writer.Write(Hex[(c >> 4) & 0xf]);
780ffe3c632Sopenharmony_ci            writer.Write(Hex[(c >> 0) & 0xf]);
781ffe3c632Sopenharmony_ci        }
782ffe3c632Sopenharmony_ci
783ffe3c632Sopenharmony_ci        /// <summary>
784ffe3c632Sopenharmony_ci        /// Settings controlling JSON formatting.
785ffe3c632Sopenharmony_ci        /// </summary>
786ffe3c632Sopenharmony_ci        public sealed class Settings
787ffe3c632Sopenharmony_ci        {
788ffe3c632Sopenharmony_ci            /// <summary>
789ffe3c632Sopenharmony_ci            /// Default settings, as used by <see cref="JsonFormatter.Default"/>
790ffe3c632Sopenharmony_ci            /// </summary>
791ffe3c632Sopenharmony_ci            public static Settings Default { get; }
792ffe3c632Sopenharmony_ci
793ffe3c632Sopenharmony_ci            // Workaround for the Mono compiler complaining about XML comments not being on
794ffe3c632Sopenharmony_ci            // valid language elements.
795ffe3c632Sopenharmony_ci            static Settings()
796ffe3c632Sopenharmony_ci            {
797ffe3c632Sopenharmony_ci                Default = new Settings(false);
798ffe3c632Sopenharmony_ci            }
799ffe3c632Sopenharmony_ci
800ffe3c632Sopenharmony_ci            /// <summary>
801ffe3c632Sopenharmony_ci            /// Whether fields which would otherwise not be included in the formatted data
802ffe3c632Sopenharmony_ci            /// should be formatted even when the value is not present, or has the default value.
803ffe3c632Sopenharmony_ci            /// This option only affects fields which don't support "presence" (e.g.
804ffe3c632Sopenharmony_ci            /// singular non-optional proto3 primitive fields).
805ffe3c632Sopenharmony_ci            /// </summary>
806ffe3c632Sopenharmony_ci            public bool FormatDefaultValues { get; }
807ffe3c632Sopenharmony_ci
808ffe3c632Sopenharmony_ci            /// <summary>
809ffe3c632Sopenharmony_ci            /// The type registry used to format <see cref="Any"/> messages.
810ffe3c632Sopenharmony_ci            /// </summary>
811ffe3c632Sopenharmony_ci            public TypeRegistry TypeRegistry { get; }
812ffe3c632Sopenharmony_ci
813ffe3c632Sopenharmony_ci            /// <summary>
814ffe3c632Sopenharmony_ci            /// Whether to format enums as ints. Defaults to false.
815ffe3c632Sopenharmony_ci            /// </summary>
816ffe3c632Sopenharmony_ci            public bool FormatEnumsAsIntegers { get; }
817ffe3c632Sopenharmony_ci
818ffe3c632Sopenharmony_ci
819ffe3c632Sopenharmony_ci            /// <summary>
820ffe3c632Sopenharmony_ci            /// Creates a new <see cref="Settings"/> object with the specified formatting of default values
821ffe3c632Sopenharmony_ci            /// and an empty type registry.
822ffe3c632Sopenharmony_ci            /// </summary>
823ffe3c632Sopenharmony_ci            /// <param name="formatDefaultValues"><c>true</c> if default values (0, empty strings etc) should be formatted; <c>false</c> otherwise.</param>
824ffe3c632Sopenharmony_ci            public Settings(bool formatDefaultValues) : this(formatDefaultValues, TypeRegistry.Empty)
825ffe3c632Sopenharmony_ci            {
826ffe3c632Sopenharmony_ci            }
827ffe3c632Sopenharmony_ci
828ffe3c632Sopenharmony_ci            /// <summary>
829ffe3c632Sopenharmony_ci            /// Creates a new <see cref="Settings"/> object with the specified formatting of default values
830ffe3c632Sopenharmony_ci            /// and type registry.
831ffe3c632Sopenharmony_ci            /// </summary>
832ffe3c632Sopenharmony_ci            /// <param name="formatDefaultValues"><c>true</c> if default values (0, empty strings etc) should be formatted; <c>false</c> otherwise.</param>
833ffe3c632Sopenharmony_ci            /// <param name="typeRegistry">The <see cref="TypeRegistry"/> to use when formatting <see cref="Any"/> messages.</param>
834ffe3c632Sopenharmony_ci            public Settings(bool formatDefaultValues, TypeRegistry typeRegistry) : this(formatDefaultValues, typeRegistry, false)
835ffe3c632Sopenharmony_ci            {
836ffe3c632Sopenharmony_ci            }
837ffe3c632Sopenharmony_ci
838ffe3c632Sopenharmony_ci            /// <summary>
839ffe3c632Sopenharmony_ci            /// Creates a new <see cref="Settings"/> object with the specified parameters.
840ffe3c632Sopenharmony_ci            /// </summary>
841ffe3c632Sopenharmony_ci            /// <param name="formatDefaultValues"><c>true</c> if default values (0, empty strings etc) should be formatted; <c>false</c> otherwise.</param>
842ffe3c632Sopenharmony_ci            /// <param name="typeRegistry">The <see cref="TypeRegistry"/> to use when formatting <see cref="Any"/> messages. TypeRegistry.Empty will be used if it is null.</param>
843ffe3c632Sopenharmony_ci            /// <param name="formatEnumsAsIntegers"><c>true</c> to format the enums as integers; <c>false</c> to format enums as enum names.</param>
844ffe3c632Sopenharmony_ci            private Settings(bool formatDefaultValues,
845ffe3c632Sopenharmony_ci                            TypeRegistry typeRegistry,
846ffe3c632Sopenharmony_ci                            bool formatEnumsAsIntegers)
847ffe3c632Sopenharmony_ci            {
848ffe3c632Sopenharmony_ci                FormatDefaultValues = formatDefaultValues;
849ffe3c632Sopenharmony_ci                TypeRegistry = typeRegistry ?? TypeRegistry.Empty;
850ffe3c632Sopenharmony_ci                FormatEnumsAsIntegers = formatEnumsAsIntegers;
851ffe3c632Sopenharmony_ci            }
852ffe3c632Sopenharmony_ci
853ffe3c632Sopenharmony_ci            /// <summary>
854ffe3c632Sopenharmony_ci            /// Creates a new <see cref="Settings"/> object with the specified formatting of default values and the current settings.
855ffe3c632Sopenharmony_ci            /// </summary>
856ffe3c632Sopenharmony_ci            /// <param name="formatDefaultValues"><c>true</c> if default values (0, empty strings etc) should be formatted; <c>false</c> otherwise.</param>
857ffe3c632Sopenharmony_ci            public Settings WithFormatDefaultValues(bool formatDefaultValues) => new Settings(formatDefaultValues, TypeRegistry, FormatEnumsAsIntegers);
858ffe3c632Sopenharmony_ci
859ffe3c632Sopenharmony_ci            /// <summary>
860ffe3c632Sopenharmony_ci            /// Creates a new <see cref="Settings"/> object with the specified type registry and the current settings.
861ffe3c632Sopenharmony_ci            /// </summary>
862ffe3c632Sopenharmony_ci            /// <param name="typeRegistry">The <see cref="TypeRegistry"/> to use when formatting <see cref="Any"/> messages.</param>
863ffe3c632Sopenharmony_ci            public Settings WithTypeRegistry(TypeRegistry typeRegistry) => new Settings(FormatDefaultValues, typeRegistry, FormatEnumsAsIntegers);
864ffe3c632Sopenharmony_ci
865ffe3c632Sopenharmony_ci            /// <summary>
866ffe3c632Sopenharmony_ci            /// Creates a new <see cref="Settings"/> object with the specified enums formatting option and the current settings.
867ffe3c632Sopenharmony_ci            /// </summary>
868ffe3c632Sopenharmony_ci            /// <param name="formatEnumsAsIntegers"><c>true</c> to format the enums as integers; <c>false</c> to format enums as enum names.</param>
869ffe3c632Sopenharmony_ci            public Settings WithFormatEnumsAsIntegers(bool formatEnumsAsIntegers) => new Settings(FormatDefaultValues, TypeRegistry, formatEnumsAsIntegers);
870ffe3c632Sopenharmony_ci        }
871ffe3c632Sopenharmony_ci
872ffe3c632Sopenharmony_ci        // Effectively a cache of mapping from enum values to the original name as specified in the proto file,
873ffe3c632Sopenharmony_ci        // fetched by reflection.
874ffe3c632Sopenharmony_ci        // The need for this is unfortunate, as is its unbounded size, but realistically it shouldn't cause issues.
875ffe3c632Sopenharmony_ci        private static class OriginalEnumValueHelper
876ffe3c632Sopenharmony_ci        {
877ffe3c632Sopenharmony_ci            // TODO: In the future we might want to use ConcurrentDictionary, at the point where all
878ffe3c632Sopenharmony_ci            // the platforms we target have it.
879ffe3c632Sopenharmony_ci            private static readonly Dictionary<System.Type, Dictionary<object, string>> dictionaries
880ffe3c632Sopenharmony_ci                = new Dictionary<System.Type, Dictionary<object, string>>();
881ffe3c632Sopenharmony_ci
882ffe3c632Sopenharmony_ci            internal static string GetOriginalName(object value)
883ffe3c632Sopenharmony_ci            {
884ffe3c632Sopenharmony_ci                var enumType = value.GetType();
885ffe3c632Sopenharmony_ci                Dictionary<object, string> nameMapping;
886ffe3c632Sopenharmony_ci                lock (dictionaries)
887ffe3c632Sopenharmony_ci                {
888ffe3c632Sopenharmony_ci                    if (!dictionaries.TryGetValue(enumType, out nameMapping))
889ffe3c632Sopenharmony_ci                    {
890ffe3c632Sopenharmony_ci                        nameMapping = GetNameMapping(enumType);
891ffe3c632Sopenharmony_ci                        dictionaries[enumType] = nameMapping;
892ffe3c632Sopenharmony_ci                    }
893ffe3c632Sopenharmony_ci                }
894ffe3c632Sopenharmony_ci
895ffe3c632Sopenharmony_ci                string originalName;
896ffe3c632Sopenharmony_ci                // If this returns false, originalName will be null, which is what we want.
897ffe3c632Sopenharmony_ci                nameMapping.TryGetValue(value, out originalName);
898ffe3c632Sopenharmony_ci                return originalName;
899ffe3c632Sopenharmony_ci            }
900ffe3c632Sopenharmony_ci
901ffe3c632Sopenharmony_ci#if NET35
902ffe3c632Sopenharmony_ci            // TODO: Consider adding functionality to TypeExtensions to avoid this difference.
903ffe3c632Sopenharmony_ci            private static Dictionary<object, string> GetNameMapping(System.Type enumType) =>
904ffe3c632Sopenharmony_ci                enumType.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static)
905ffe3c632Sopenharmony_ci                    .Where(f => (f.GetCustomAttributes(typeof(OriginalNameAttribute), false)
906ffe3c632Sopenharmony_ci                                 .FirstOrDefault() as OriginalNameAttribute)
907ffe3c632Sopenharmony_ci                                 ?.PreferredAlias ?? true)
908ffe3c632Sopenharmony_ci                    .ToDictionary(f => f.GetValue(null),
909ffe3c632Sopenharmony_ci                                  f => (f.GetCustomAttributes(typeof(OriginalNameAttribute), false)
910ffe3c632Sopenharmony_ci                                        .FirstOrDefault() as OriginalNameAttribute)
911ffe3c632Sopenharmony_ci                                        // If the attribute hasn't been applied, fall back to the name of the field.
912ffe3c632Sopenharmony_ci                                        ?.Name ?? f.Name);
913ffe3c632Sopenharmony_ci#else
914ffe3c632Sopenharmony_ci            private static Dictionary<object, string> GetNameMapping(System.Type enumType) =>
915ffe3c632Sopenharmony_ci                enumType.GetTypeInfo().DeclaredFields
916ffe3c632Sopenharmony_ci                    .Where(f => f.IsStatic)
917ffe3c632Sopenharmony_ci                    .Where(f => f.GetCustomAttributes<OriginalNameAttribute>()
918ffe3c632Sopenharmony_ci                                 .FirstOrDefault()?.PreferredAlias ?? true)
919ffe3c632Sopenharmony_ci                    .ToDictionary(f => f.GetValue(null),
920ffe3c632Sopenharmony_ci                                  f => f.GetCustomAttributes<OriginalNameAttribute>()
921ffe3c632Sopenharmony_ci                                        .FirstOrDefault()
922ffe3c632Sopenharmony_ci                                        // If the attribute hasn't been applied, fall back to the name of the field.
923ffe3c632Sopenharmony_ci                                        ?.Name ?? f.Name);
924ffe3c632Sopenharmony_ci#endif
925ffe3c632Sopenharmony_ci        }
926ffe3c632Sopenharmony_ci    }
927ffe3c632Sopenharmony_ci}
928