Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.ExtLibs/HeuristicLab.ProtobufCS/2.4.1/ProtobufCS/src/ProtoGen/MessageGenerator.cs @ 8295

Last change on this file since 8295 was 8295, checked in by abeham, 12 years ago

#1897:

  • Removed protocol buffers 0.9.1
  • Added protocol buffers 2.4.1
  • Updated proto processing command
File size: 42.1 KB
Line 
1#region Copyright notice and license
2
3// Protocol Buffers - Google's data interchange format
4// Copyright 2008 Google Inc.  All rights reserved.
5// http://github.com/jskeet/dotnet-protobufs/
6// Original C++/Java/Python code:
7// http://code.google.com/p/protobuf/
8//
9// Redistribution and use in source and binary forms, with or without
10// modification, are permitted provided that the following conditions are
11// met:
12//
13//     * Redistributions of source code must retain the above copyright
14// notice, this list of conditions and the following disclaimer.
15//     * Redistributions in binary form must reproduce the above
16// copyright notice, this list of conditions and the following disclaimer
17// in the documentation and/or other materials provided with the
18// distribution.
19//     * Neither the name of Google Inc. nor the names of its
20// contributors may be used to endorse or promote products derived from
21// this software without specific prior written permission.
22//
23// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34
35#endregion
36
37using System;
38using System.Collections.Generic;
39using Google.ProtocolBuffers.DescriptorProtos;
40using Google.ProtocolBuffers.Descriptors;
41
42namespace Google.ProtocolBuffers.ProtoGen
43{
44    internal class MessageGenerator : SourceGeneratorBase<MessageDescriptor>, ISourceGenerator
45    {
46        private string[] _fieldNames;
47
48        internal MessageGenerator(MessageDescriptor descriptor) : base(descriptor)
49        {
50        }
51
52        private string ClassName
53        {
54            get { return Descriptor.Name; }
55        }
56
57        private string FullClassName
58        {
59            get { return GetClassName(Descriptor); }
60        }
61
62        /// <summary>
63        /// Get an identifier that uniquely identifies this type within the file.
64        /// This is used to declare static variables related to this type at the
65        /// outermost file scope.
66        /// </summary>
67        private static string GetUniqueFileScopeIdentifier(IDescriptor descriptor)
68        {
69            return "static_" + descriptor.FullName.Replace(".", "_");
70        }
71
72        internal void GenerateStaticVariables(TextGenerator writer)
73        {
74            // Because descriptor.proto (Google.ProtocolBuffers.DescriptorProtos) is
75            // used in the construction of descriptors, we have a tricky bootstrapping
76            // problem.  To help control static initialization order, we make sure all
77            // descriptors and other static data that depends on them are members of
78            // the proto-descriptor class.  This way, they will be initialized in
79            // a deterministic order.
80
81            string identifier = GetUniqueFileScopeIdentifier(Descriptor);
82
83            if (!UseLiteRuntime)
84            {
85                // The descriptor for this type.
86                string access = Descriptor.File.CSharpOptions.NestClasses ? "private" : "internal";
87                writer.WriteLine("{0} static pbd::MessageDescriptor internal__{1}__Descriptor;", access, identifier);
88                writer.WriteLine(
89                    "{0} static pb::FieldAccess.FieldAccessorTable<{1}, {1}.Builder> internal__{2}__FieldAccessorTable;",
90                    access, FullClassName, identifier);
91            }
92            // Generate static members for all nested types.
93            foreach (MessageDescriptor nestedMessage in Descriptor.NestedTypes)
94            {
95                new MessageGenerator(nestedMessage).GenerateStaticVariables(writer);
96            }
97        }
98
99        internal void GenerateStaticVariableInitializers(TextGenerator writer)
100        {
101            string identifier = GetUniqueFileScopeIdentifier(Descriptor);
102
103            if (!UseLiteRuntime)
104            {
105                writer.Write("internal__{0}__Descriptor = ", identifier);
106                if (Descriptor.ContainingType == null)
107                {
108                    writer.WriteLine("Descriptor.MessageTypes[{0}];", Descriptor.Index);
109                }
110                else
111                {
112                    writer.WriteLine("internal__{0}__Descriptor.NestedTypes[{1}];",
113                                     GetUniqueFileScopeIdentifier(Descriptor.ContainingType), Descriptor.Index);
114                }
115
116                writer.WriteLine("internal__{0}__FieldAccessorTable = ", identifier);
117                writer.WriteLine(
118                    "    new pb::FieldAccess.FieldAccessorTable<{1}, {1}.Builder>(internal__{0}__Descriptor,",
119                    identifier, FullClassName);
120                writer.Print("        new string[] { ");
121                foreach (FieldDescriptor field in Descriptor.Fields)
122                {
123                    writer.Write("\"{0}\", ", field.CSharpOptions.PropertyName);
124                }
125                writer.WriteLine("});");
126            }
127
128            // Generate static member initializers for all nested types.
129            foreach (MessageDescriptor nestedMessage in Descriptor.NestedTypes)
130            {
131                new MessageGenerator(nestedMessage).GenerateStaticVariableInitializers(writer);
132            }
133
134            foreach (FieldDescriptor extension in Descriptor.Extensions)
135            {
136                new ExtensionGenerator(extension).GenerateStaticVariableInitializers(writer);
137            }
138        }
139
140        public string[] FieldNames
141        {
142            get
143            {
144                if (_fieldNames == null)
145                {
146                    List<string> names = new List<string>();
147                    foreach (FieldDescriptor fieldDescriptor in Descriptor.Fields)
148                    {
149                        names.Add(fieldDescriptor.Name);
150                    }
151                    //if you change this, the search must also change in GenerateBuilderParsingMethods
152                    names.Sort(StringComparer.Ordinal);
153                    _fieldNames = names.ToArray();
154                }
155                return _fieldNames;
156            }
157        }
158
159        internal int FieldOrdinal(FieldDescriptor field)
160        {
161            return Array.BinarySearch(FieldNames, field.Name, StringComparer.Ordinal);
162        }
163
164        private IFieldSourceGenerator CreateFieldGenerator(FieldDescriptor fieldDescriptor)
165        {
166            return SourceGenerators.CreateFieldGenerator(fieldDescriptor, FieldOrdinal(fieldDescriptor));
167        }
168       
169        public void Generate(TextGenerator writer)
170        {
171            if (Descriptor.File.CSharpOptions.AddSerializable)
172            {
173                writer.WriteLine("[global::System.SerializableAttribute()]");
174            }
175            writer.WriteLine("[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]");
176            writer.WriteLine("[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]");
177            writer.WriteLine("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"{0}\", \"{1}\")]",
178                             GetType().Assembly.GetName().Name, GetType().Assembly.GetName().Version);
179            writer.WriteLine("{0} sealed partial class {1} : pb::{2}Message{3}<{1}, {1}.Builder> {{",
180                             ClassAccessLevel, ClassName,
181                             Descriptor.Proto.ExtensionRangeCount > 0 ? "Extendable" : "Generated",
182                             RuntimeSuffix);
183            writer.Indent();
184            if (Descriptor.File.CSharpOptions.GeneratePrivateCtor)
185            {
186                writer.WriteLine("private {0}() {{ }}", ClassName);
187            }
188            // Must call MakeReadOnly() to make sure all lists are made read-only
189            writer.WriteLine("private static readonly {0} defaultInstance = new {0}().MakeReadOnly();", ClassName);
190
191            if (OptimizeSpeed)
192            {
193                writer.WriteLine("private static readonly string[] _{0}FieldNames = new string[] {{ {2}{1}{2} }};",
194                                 NameHelpers.UnderscoresToCamelCase(ClassName), String.Join("\", \"", FieldNames),
195                                 FieldNames.Length > 0 ? "\"" : "");
196                List<string> tags = new List<string>();
197                foreach (string name in FieldNames)
198                {
199                    tags.Add(WireFormat.MakeTag(Descriptor.FindFieldByName(name)).ToString());
200                }
201
202                writer.WriteLine("private static readonly uint[] _{0}FieldTags = new uint[] {{ {1} }};",
203                                 NameHelpers.UnderscoresToCamelCase(ClassName), String.Join(", ", tags.ToArray()));
204            }
205            writer.WriteLine("public static {0} DefaultInstance {{", ClassName);
206            writer.WriteLine("  get { return defaultInstance; }");
207            writer.WriteLine("}");
208            writer.WriteLine();
209            writer.WriteLine("public override {0} DefaultInstanceForType {{", ClassName);
210            writer.WriteLine("  get { return DefaultInstance; }");
211            writer.WriteLine("}");
212            writer.WriteLine();
213            writer.WriteLine("protected override {0} ThisMessage {{", ClassName);
214            writer.WriteLine("  get { return this; }");
215            writer.WriteLine("}");
216            writer.WriteLine();
217            if (!UseLiteRuntime)
218            {
219                writer.WriteLine("public static pbd::MessageDescriptor Descriptor {");
220                writer.WriteLine("  get {{ return {0}.internal__{1}__Descriptor; }}",
221                                 DescriptorUtil.GetFullUmbrellaClassName(Descriptor),
222                                 GetUniqueFileScopeIdentifier(Descriptor));
223                writer.WriteLine("}");
224                writer.WriteLine();
225                writer.WriteLine(
226                    "protected override pb::FieldAccess.FieldAccessorTable<{0}, {0}.Builder> InternalFieldAccessors {{",
227                    ClassName);
228                writer.WriteLine("  get {{ return {0}.internal__{1}__FieldAccessorTable; }}",
229                                 DescriptorUtil.GetFullUmbrellaClassName(Descriptor),
230                                 GetUniqueFileScopeIdentifier(Descriptor));
231                writer.WriteLine("}");
232                writer.WriteLine();
233            }
234
235            // Extensions don't need to go in an extra nested type
236            WriteChildren(writer, null, Descriptor.Extensions);
237
238            if (Descriptor.EnumTypes.Count + Descriptor.NestedTypes.Count > 0)
239            {
240                writer.WriteLine("#region Nested types");
241                writer.WriteLine("[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]");
242                writer.WriteLine("[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]");
243                writer.WriteLine("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"{0}\", \"{1}\")]",
244                                 GetType().Assembly.GetName().Name, GetType().Assembly.GetName().Version);
245                writer.WriteLine("public static class Types {");
246                writer.Indent();
247                WriteChildren(writer, null, Descriptor.EnumTypes);
248                WriteChildren(writer, null, Descriptor.NestedTypes);
249                writer.Outdent();
250                writer.WriteLine("}");
251                writer.WriteLine("#endregion");
252                writer.WriteLine();
253            }
254
255            foreach (FieldDescriptor fieldDescriptor in Descriptor.Fields)
256            {
257                if (Descriptor.File.CSharpOptions.ClsCompliance && GetFieldConstantName(fieldDescriptor).StartsWith("_"))
258                {
259                    writer.WriteLine("[global::System.CLSCompliant(false)]");
260                }
261
262                // Rats: we lose the debug comment here :(
263                writer.WriteLine("public const int {0} = {1};", GetFieldConstantName(fieldDescriptor),
264                                 fieldDescriptor.FieldNumber);
265                CreateFieldGenerator(fieldDescriptor).GenerateMembers(writer);
266                writer.WriteLine();
267            }
268
269            if (OptimizeSpeed)
270            {
271                GenerateIsInitialized(writer);
272                GenerateMessageSerializationMethods(writer);
273            }
274            if (UseLiteRuntime)
275            {
276                GenerateLiteRuntimeMethods(writer);
277            }
278
279            GenerateParseFromMethods(writer);
280            GenerateBuilder(writer);
281
282            // Force the static initialization code for the file to run, since it may
283            // initialize static variables declared in this class.
284            writer.WriteLine("static {0}() {{", ClassName);
285            // We call object.ReferenceEquals() just to make it a valid statement on its own.
286            // Another option would be GetType(), but that causes problems in DescriptorProtoFile,
287            // where the bootstrapping is somewhat recursive - type initializers call
288            // each other, effectively. We temporarily see Descriptor as null.
289            writer.WriteLine("  object.ReferenceEquals({0}.Descriptor, null);",
290                             DescriptorUtil.GetFullUmbrellaClassName(Descriptor));
291            writer.WriteLine("}");
292
293            writer.Outdent();
294            writer.WriteLine("}");
295            writer.WriteLine();
296        }
297
298        private void GenerateLiteRuntimeMethods(TextGenerator writer)
299        {
300            bool callbase = Descriptor.Proto.ExtensionRangeCount > 0;
301            writer.WriteLine("#region Lite runtime methods");
302            writer.WriteLine("public override int GetHashCode() {");
303            writer.Indent();
304            writer.WriteLine("int hash = GetType().GetHashCode();");
305            foreach (FieldDescriptor fieldDescriptor in Descriptor.Fields)
306            {
307                CreateFieldGenerator(fieldDescriptor).WriteHash(writer);
308            }
309            if (callbase)
310            {
311                writer.WriteLine("hash ^= base.GetHashCode();");
312            }
313            writer.WriteLine("return hash;");
314            writer.Outdent();
315            writer.WriteLine("}");
316            writer.WriteLine();
317
318            writer.WriteLine("public override bool Equals(object obj) {");
319            writer.Indent();
320            writer.WriteLine("{0} other = obj as {0};", ClassName);
321            writer.WriteLine("if (other == null) return false;");
322            foreach (FieldDescriptor fieldDescriptor in Descriptor.Fields)
323            {
324                CreateFieldGenerator(fieldDescriptor).WriteEquals(writer);
325            }
326            if (callbase)
327            {
328                writer.WriteLine("if (!base.Equals(other)) return false;");
329            }
330            writer.WriteLine("return true;");
331            writer.Outdent();
332            writer.WriteLine("}");
333            writer.WriteLine();
334
335            writer.WriteLine("public override void PrintTo(global::System.IO.TextWriter writer) {");
336            writer.Indent();
337            List<FieldDescriptor> sorted = new List<FieldDescriptor>(Descriptor.Fields);
338            sorted.Sort(
339                new Comparison<FieldDescriptor>(
340                    delegate(FieldDescriptor a, FieldDescriptor b) { return a.FieldNumber.CompareTo(b.FieldNumber); }));
341            foreach (FieldDescriptor fieldDescriptor in sorted)
342            {
343                CreateFieldGenerator(fieldDescriptor).WriteToString(writer);
344            }
345            if (callbase)
346            {
347                writer.WriteLine("base.PrintTo(writer);");
348            }
349            writer.Outdent();
350            writer.WriteLine("}");
351            writer.WriteLine("#endregion");
352            writer.WriteLine();
353        }
354
355        private void GenerateMessageSerializationMethods(TextGenerator writer)
356        {
357            List<FieldDescriptor> sortedFields = new List<FieldDescriptor>(Descriptor.Fields);
358            sortedFields.Sort((f1, f2) => f1.FieldNumber.CompareTo(f2.FieldNumber));
359
360            List<DescriptorProto.Types.ExtensionRange> sortedExtensions =
361                new List<DescriptorProto.Types.ExtensionRange>(Descriptor.Proto.ExtensionRangeList);
362            sortedExtensions.Sort((r1, r2) => (r1.Start.CompareTo(r2.Start)));
363
364            writer.WriteLine("public override void WriteTo(pb::ICodedOutputStream output) {");
365            writer.Indent();
366            // Make sure we've computed the serialized length, so that packed fields are generated correctly.
367            writer.WriteLine("int size = SerializedSize;");
368            writer.WriteLine("string[] field_names = _{0}FieldNames;", NameHelpers.UnderscoresToCamelCase(ClassName));
369            if (Descriptor.Proto.ExtensionRangeList.Count > 0)
370            {
371                writer.WriteLine(
372                    "pb::ExtendableMessage{1}<{0}, {0}.Builder>.ExtensionWriter extensionWriter = CreateExtensionWriter(this);",
373                    ClassName, RuntimeSuffix);
374            }
375
376            // Merge the fields and the extension ranges, both sorted by field number.
377            for (int i = 0, j = 0; i < Descriptor.Fields.Count || j < sortedExtensions.Count;)
378            {
379                if (i == Descriptor.Fields.Count)
380                {
381                    GenerateSerializeOneExtensionRange(writer, sortedExtensions[j++]);
382                }
383                else if (j == sortedExtensions.Count)
384                {
385                    GenerateSerializeOneField(writer, sortedFields[i++]);
386                }
387                else if (sortedFields[i].FieldNumber < sortedExtensions[j].Start)
388                {
389                    GenerateSerializeOneField(writer, sortedFields[i++]);
390                }
391                else
392                {
393                    GenerateSerializeOneExtensionRange(writer, sortedExtensions[j++]);
394                }
395            }
396
397            if (!UseLiteRuntime)
398            {
399                if (Descriptor.Proto.Options.MessageSetWireFormat)
400                {
401                    writer.WriteLine("UnknownFields.WriteAsMessageSetTo(output);");
402                }
403                else
404                {
405                    writer.WriteLine("UnknownFields.WriteTo(output);");
406                }
407            }
408
409            writer.Outdent();
410            writer.WriteLine("}");
411            writer.WriteLine();
412            writer.WriteLine("private int memoizedSerializedSize = -1;");
413            writer.WriteLine("public override int SerializedSize {");
414            writer.Indent();
415            writer.WriteLine("get {");
416            writer.Indent();
417            writer.WriteLine("int size = memoizedSerializedSize;");
418            writer.WriteLine("if (size != -1) return size;");
419            writer.WriteLine();
420            writer.WriteLine("size = 0;");
421            foreach (FieldDescriptor field in Descriptor.Fields)
422            {
423                CreateFieldGenerator(field).GenerateSerializedSizeCode(writer);
424            }
425            if (Descriptor.Proto.ExtensionRangeCount > 0)
426            {
427                writer.WriteLine("size += ExtensionsSerializedSize;");
428            }
429
430            if (!UseLiteRuntime)
431            {
432                if (Descriptor.Options.MessageSetWireFormat)
433                {
434                    writer.WriteLine("size += UnknownFields.SerializedSizeAsMessageSet;");
435                }
436                else
437                {
438                    writer.WriteLine("size += UnknownFields.SerializedSize;");
439                }
440            }
441            writer.WriteLine("memoizedSerializedSize = size;");
442            writer.WriteLine("return size;");
443            writer.Outdent();
444            writer.WriteLine("}");
445            writer.Outdent();
446            writer.WriteLine("}");
447            writer.WriteLine();
448        }
449
450        private void GenerateSerializeOneField(TextGenerator writer, FieldDescriptor fieldDescriptor)
451        {
452            CreateFieldGenerator(fieldDescriptor).GenerateSerializationCode(writer);
453        }
454
455        private static void GenerateSerializeOneExtensionRange(TextGenerator writer,
456                                                               DescriptorProto.Types.ExtensionRange extensionRange)
457        {
458            writer.WriteLine("extensionWriter.WriteUntil({0}, output);", extensionRange.End);
459        }
460
461        private void GenerateParseFromMethods(TextGenerator writer)
462        {
463            // Note:  These are separate from GenerateMessageSerializationMethods()
464            //   because they need to be generated even for messages that are optimized
465            //   for code size.
466
467            writer.WriteLine("public static {0} ParseFrom(pb::ByteString data) {{", ClassName);
468            writer.WriteLine("  return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed();");
469            writer.WriteLine("}");
470            writer.WriteLine(
471                "public static {0} ParseFrom(pb::ByteString data, pb::ExtensionRegistry extensionRegistry) {{",
472                ClassName);
473            writer.WriteLine("  return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();");
474            writer.WriteLine("}");
475            writer.WriteLine("public static {0} ParseFrom(byte[] data) {{", ClassName);
476            writer.WriteLine("  return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed();");
477            writer.WriteLine("}");
478            writer.WriteLine("public static {0} ParseFrom(byte[] data, pb::ExtensionRegistry extensionRegistry) {{",
479                             ClassName);
480            writer.WriteLine("  return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();");
481            writer.WriteLine("}");
482            writer.WriteLine("public static {0} ParseFrom(global::System.IO.Stream input) {{", ClassName);
483            writer.WriteLine("  return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed();");
484            writer.WriteLine("}");
485            writer.WriteLine(
486                "public static {0} ParseFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) {{",
487                ClassName);
488            writer.WriteLine("  return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();");
489            writer.WriteLine("}");
490            writer.WriteLine("public static {0} ParseDelimitedFrom(global::System.IO.Stream input) {{", ClassName);
491            writer.WriteLine("  return CreateBuilder().MergeDelimitedFrom(input).BuildParsed();");
492            writer.WriteLine("}");
493            writer.WriteLine(
494                "public static {0} ParseDelimitedFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) {{",
495                ClassName);
496            writer.WriteLine("  return CreateBuilder().MergeDelimitedFrom(input, extensionRegistry).BuildParsed();");
497            writer.WriteLine("}");
498            writer.WriteLine("public static {0} ParseFrom(pb::ICodedInputStream input) {{", ClassName);
499            writer.WriteLine("  return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed();");
500            writer.WriteLine("}");
501            writer.WriteLine(
502                "public static {0} ParseFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) {{",
503                ClassName);
504            writer.WriteLine("  return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();");
505            writer.WriteLine("}");
506        }
507
508        /// <summary>
509        /// Returns whether or not the specified message type has any required fields.
510        /// If it doesn't, calls to check for initialization can be optimised.
511        /// TODO(jonskeet): Move this into MessageDescriptor?
512        /// </summary>
513        private static bool HasRequiredFields(MessageDescriptor descriptor,
514                                              Dictionary<MessageDescriptor, object> alreadySeen)
515        {
516            if (alreadySeen.ContainsKey(descriptor))
517            {
518                // The type is already in cache.  This means that either:
519                // a. The type has no required fields.
520                // b. We are in the midst of checking if the type has required fields,
521                //    somewhere up the stack.  In this case, we know that if the type
522                //    has any required fields, they'll be found when we return to it,
523                //    and the whole call to HasRequiredFields() will return true.
524                //    Therefore, we don't have to check if this type has required fields
525                //    here.
526                return false;
527            }
528            alreadySeen[descriptor] = descriptor; // Value is irrelevant
529
530            // If the type has extensions, an extension with message type could contain
531            // required fields, so we have to be conservative and assume such an
532            // extension exists.
533            if (descriptor.Extensions.Count > 0)
534            {
535                return true;
536            }
537
538            foreach (FieldDescriptor field in descriptor.Fields)
539            {
540                if (field.IsRequired)
541                {
542                    return true;
543                }
544                // Message or group
545                if (field.MappedType == MappedType.Message)
546                {
547                    if (HasRequiredFields(field.MessageType, alreadySeen))
548                    {
549                        return true;
550                    }
551                }
552            }
553            return false;
554        }
555
556        private void GenerateBuilder(TextGenerator writer)
557        {
558            writer.WriteLine("private {0} MakeReadOnly() {{", ClassName);
559            writer.Indent();
560            foreach (FieldDescriptor field in Descriptor.Fields)
561            {
562                CreateFieldGenerator(field).GenerateBuildingCode(writer);
563            }
564            writer.WriteLine("return this;");
565            writer.Outdent();
566            writer.WriteLine("}");
567            writer.WriteLine();
568
569            writer.WriteLine("public static Builder CreateBuilder() { return new Builder(); }");
570            writer.WriteLine("public override Builder ToBuilder() { return CreateBuilder(this); }");
571            writer.WriteLine("public override Builder CreateBuilderForType() { return new Builder(); }");
572            writer.WriteLine("public static Builder CreateBuilder({0} prototype) {{", ClassName);
573            writer.WriteLine("  return new Builder(prototype);");
574            writer.WriteLine("}");
575            writer.WriteLine();
576            if (Descriptor.File.CSharpOptions.AddSerializable)
577            {
578                writer.WriteLine("[global::System.SerializableAttribute()]");
579            }
580            writer.WriteLine("[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]");
581            writer.WriteLine("[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]");
582            writer.WriteLine("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"{0}\", \"{1}\")]",
583                             GetType().Assembly.GetName().Name, GetType().Assembly.GetName().Version);
584            writer.WriteLine("{0} sealed partial class Builder : pb::{2}Builder{3}<{1}, Builder> {{",
585                             ClassAccessLevel, ClassName,
586                             Descriptor.Proto.ExtensionRangeCount > 0 ? "Extendable" : "Generated", RuntimeSuffix);
587            writer.Indent();
588            writer.WriteLine("protected override Builder ThisBuilder {");
589            writer.WriteLine("  get { return this; }");
590            writer.WriteLine("}");
591            GenerateCommonBuilderMethods(writer);
592            if (OptimizeSpeed)
593            {
594                GenerateBuilderParsingMethods(writer);
595            }
596            foreach (FieldDescriptor field in Descriptor.Fields)
597            {
598                writer.WriteLine();
599                // No field comment :(
600                CreateFieldGenerator(field).GenerateBuilderMembers(writer);
601            }
602            writer.Outdent();
603            writer.WriteLine("}");
604        }
605
606        private void GenerateCommonBuilderMethods(TextGenerator writer)
607        {
608            //default constructor
609            writer.WriteLine("public Builder() {");
610            //Durring static initialization of message, DefaultInstance is expected to return null.
611            writer.WriteLine("  result = DefaultInstance;");
612            writer.WriteLine("  resultIsReadOnly = true;");
613            writer.WriteLine("}");
614            //clone constructor
615            writer.WriteLine("internal Builder({0} cloneFrom) {{", ClassName);
616            writer.WriteLine("  result = cloneFrom;");
617            writer.WriteLine("  resultIsReadOnly = true;");
618            writer.WriteLine("}");
619            writer.WriteLine();
620            writer.WriteLine("private bool resultIsReadOnly;");
621            writer.WriteLine("private {0} result;", ClassName);
622            writer.WriteLine();
623            writer.WriteLine("private {0} PrepareBuilder() {{", ClassName);
624            writer.WriteLine("  if (resultIsReadOnly) {");
625            writer.WriteLine("    {0} original = result;", ClassName);
626            writer.WriteLine("    result = new {0}();", ClassName);
627            writer.WriteLine("    resultIsReadOnly = false;");
628            writer.WriteLine("    MergeFrom(original);");
629            writer.WriteLine("  }");
630            writer.WriteLine("  return result;");
631            writer.WriteLine("}");
632            writer.WriteLine();
633            writer.WriteLine("public override bool IsInitialized {");
634            writer.WriteLine("  get { return result.IsInitialized; }");
635            writer.WriteLine("}");
636            writer.WriteLine();
637            writer.WriteLine("protected override {0} MessageBeingBuilt {{", ClassName);
638            writer.WriteLine("  get { return PrepareBuilder(); }");
639            writer.WriteLine("}");
640            writer.WriteLine();
641            //Not actually expecting that DefaultInstance would ever be null here; however, we will ensure it does not break
642            writer.WriteLine("public override Builder Clear() {");
643            writer.WriteLine("  result = DefaultInstance;", ClassName);
644            writer.WriteLine("  resultIsReadOnly = true;");
645            writer.WriteLine("  return this;");
646            writer.WriteLine("}");
647            writer.WriteLine();
648            writer.WriteLine("public override Builder Clone() {");
649            writer.WriteLine("  if (resultIsReadOnly) {");
650            writer.WriteLine("    return new Builder(result);");
651            writer.WriteLine("  } else {");
652            writer.WriteLine("    return new Builder().MergeFrom(result);");
653            writer.WriteLine("  }");
654            writer.WriteLine("}");
655            writer.WriteLine();
656            if (!UseLiteRuntime)
657            {
658                writer.WriteLine("public override pbd::MessageDescriptor DescriptorForType {");
659                writer.WriteLine("  get {{ return {0}.Descriptor; }}", FullClassName);
660                writer.WriteLine("}");
661                writer.WriteLine();
662            }
663            writer.WriteLine("public override {0} DefaultInstanceForType {{", ClassName);
664            writer.WriteLine("  get {{ return {0}.DefaultInstance; }}", FullClassName);
665            writer.WriteLine("}");
666            writer.WriteLine();
667
668            writer.WriteLine("public override {0} BuildPartial() {{", ClassName);
669            writer.Indent();
670            writer.WriteLine("if (resultIsReadOnly) {");
671            writer.WriteLine("  return result;");
672            writer.WriteLine("}");
673            writer.WriteLine("resultIsReadOnly = true;");
674            writer.WriteLine("return result.MakeReadOnly();");
675            writer.Outdent();
676            writer.WriteLine("}");
677            writer.WriteLine();
678
679            if (OptimizeSpeed)
680            {
681                writer.WriteLine("public override Builder MergeFrom(pb::IMessage{0} other) {{", RuntimeSuffix);
682                writer.WriteLine("  if (other is {0}) {{", ClassName);
683                writer.WriteLine("    return MergeFrom(({0}) other);", ClassName);
684                writer.WriteLine("  } else {");
685                writer.WriteLine("    base.MergeFrom(other);");
686                writer.WriteLine("    return this;");
687                writer.WriteLine("  }");
688                writer.WriteLine("}");
689                writer.WriteLine();
690                writer.WriteLine("public override Builder MergeFrom({0} other) {{", ClassName);
691                // Optimization:  If other is the default instance, we know none of its
692                // fields are set so we can skip the merge.
693                writer.Indent();
694                writer.WriteLine("if (other == {0}.DefaultInstance) return this;", FullClassName);
695                writer.WriteLine("PrepareBuilder();");
696                foreach (FieldDescriptor field in Descriptor.Fields)
697                {
698                    CreateFieldGenerator(field).GenerateMergingCode(writer);
699                }
700                // if message type has extensions
701                if (Descriptor.Proto.ExtensionRangeCount > 0)
702                {
703                    writer.WriteLine("  this.MergeExtensionFields(other);");
704                }
705                if (!UseLiteRuntime)
706                {
707                    writer.WriteLine("this.MergeUnknownFields(other.UnknownFields);");
708                }
709                writer.WriteLine("return this;");
710                writer.Outdent();
711                writer.WriteLine("}");
712                writer.WriteLine();
713            }
714        }
715
716        private void GenerateBuilderParsingMethods(TextGenerator writer)
717        {
718            List<FieldDescriptor> sortedFields = new List<FieldDescriptor>(Descriptor.Fields);
719            sortedFields.Sort((f1, f2) => f1.FieldNumber.CompareTo(f2.FieldNumber));
720
721            writer.WriteLine("public override Builder MergeFrom(pb::ICodedInputStream input) {");
722            writer.WriteLine("  return MergeFrom(input, pb::ExtensionRegistry.Empty);");
723            writer.WriteLine("}");
724            writer.WriteLine();
725            writer.WriteLine(
726                "public override Builder MergeFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) {");
727            writer.Indent();
728            writer.WriteLine("PrepareBuilder();");
729            if (!UseLiteRuntime)
730            {
731                writer.WriteLine("pb::UnknownFieldSet.Builder unknownFields = null;");
732            }
733            writer.WriteLine("uint tag;");
734            writer.WriteLine("string field_name;");
735            writer.WriteLine("while (input.ReadTag(out tag, out field_name)) {");
736            writer.Indent();
737            writer.WriteLine("if(tag == 0 && field_name != null) {");
738            writer.Indent();
739            //if you change from StringComparer.Ordinal, the array sort in FieldNames { get; } must also change
740            writer.WriteLine(
741                "int field_ordinal = global::System.Array.BinarySearch(_{0}FieldNames, field_name, global::System.StringComparer.Ordinal);",
742                NameHelpers.UnderscoresToCamelCase(ClassName));
743            writer.WriteLine("if(field_ordinal >= 0)");
744            writer.WriteLine("  tag = _{0}FieldTags[field_ordinal];", NameHelpers.UnderscoresToCamelCase(ClassName));
745            writer.WriteLine("else {");
746            if (!UseLiteRuntime)
747            {
748                writer.WriteLine("  if (unknownFields == null) {"); // First unknown field - create builder now
749                writer.WriteLine("    unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields);");
750                writer.WriteLine("  }");
751            }
752            writer.WriteLine("  ParseUnknownField(input, {0}extensionRegistry, tag, field_name);",
753                             UseLiteRuntime ? "" : "unknownFields, ");
754            writer.WriteLine("  continue;");
755            writer.WriteLine("}");
756            writer.Outdent();
757            writer.WriteLine("}");
758
759            writer.WriteLine("switch (tag) {");
760            writer.Indent();
761            writer.WriteLine("case 0: {"); // 0 signals EOF / limit reached
762            writer.WriteLine("  throw pb::InvalidProtocolBufferException.InvalidTag();");
763            writer.WriteLine("}");
764            writer.WriteLine("default: {");
765            writer.WriteLine("  if (pb::WireFormat.IsEndGroupTag(tag)) {");
766            if (!UseLiteRuntime)
767            {
768                writer.WriteLine("    if (unknownFields != null) {");
769                writer.WriteLine("      this.UnknownFields = unknownFields.Build();");
770                writer.WriteLine("    }");
771            }
772            writer.WriteLine("    return this;"); // it's an endgroup tag
773            writer.WriteLine("  }");
774            if (!UseLiteRuntime)
775            {
776                writer.WriteLine("  if (unknownFields == null) {"); // First unknown field - create builder now
777                writer.WriteLine("    unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields);");
778                writer.WriteLine("  }");
779            }
780            writer.WriteLine("  ParseUnknownField(input, {0}extensionRegistry, tag, field_name);",
781                             UseLiteRuntime ? "" : "unknownFields, ");
782            writer.WriteLine("  break;");
783            writer.WriteLine("}");
784            foreach (FieldDescriptor field in sortedFields)
785            {
786                WireFormat.WireType wt = WireFormat.GetWireType(field.FieldType);
787                uint tag = WireFormat.MakeTag(field.FieldNumber, wt);
788
789                if (field.IsRepeated &&
790                    (wt == WireFormat.WireType.Varint || wt == WireFormat.WireType.Fixed32 ||
791                     wt == WireFormat.WireType.Fixed64))
792                {
793                    writer.WriteLine("case {0}:",
794                                     WireFormat.MakeTag(field.FieldNumber, WireFormat.WireType.LengthDelimited));
795                }
796
797                writer.WriteLine("case {0}: {{", tag);
798                writer.Indent();
799                CreateFieldGenerator(field).GenerateParsingCode(writer);
800                writer.WriteLine("break;");
801                writer.Outdent();
802                writer.WriteLine("}");
803            }
804            writer.Outdent();
805            writer.WriteLine("}");
806            writer.Outdent();
807            writer.WriteLine("}");
808            writer.WriteLine();
809            if (!UseLiteRuntime)
810            {
811                writer.WriteLine("if (unknownFields != null) {");
812                writer.WriteLine("  this.UnknownFields = unknownFields.Build();");
813                writer.WriteLine("}");
814            }
815            writer.WriteLine("return this;");
816            writer.Outdent();
817            writer.WriteLine("}");
818            writer.WriteLine();
819        }
820
821        private void GenerateIsInitialized(TextGenerator writer)
822        {
823            writer.WriteLine("public override bool IsInitialized {");
824            writer.Indent();
825            writer.WriteLine("get {");
826            writer.Indent();
827
828            // Check that all required fields in this message are set.
829            // TODO(kenton):  We can optimize this when we switch to putting all the
830            // "has" fields into a single bitfield.
831            foreach (FieldDescriptor field in Descriptor.Fields)
832            {
833                if (field.IsRequired)
834                {
835                    writer.WriteLine("if (!has{0}) return false;", field.CSharpOptions.PropertyName);
836                }
837            }
838
839            // Now check that all embedded messages are initialized.
840            foreach (FieldDescriptor field in Descriptor.Fields)
841            {
842                if (field.FieldType != FieldType.Message ||
843                    !HasRequiredFields(field.MessageType, new Dictionary<MessageDescriptor, object>()))
844                {
845                    continue;
846                }
847                string propertyName = NameHelpers.UnderscoresToPascalCase(GetFieldName(field));
848                if (field.IsRepeated)
849                {
850                    writer.WriteLine("foreach ({0} element in {1}List) {{", GetClassName(field.MessageType),
851                                     propertyName);
852                    writer.WriteLine("  if (!element.IsInitialized) return false;");
853                    writer.WriteLine("}");
854                }
855                else if (field.IsOptional)
856                {
857                    writer.WriteLine("if (Has{0}) {{", propertyName);
858                    writer.WriteLine("  if (!{0}.IsInitialized) return false;", propertyName);
859                    writer.WriteLine("}");
860                }
861                else
862                {
863                    writer.WriteLine("if (!{0}.IsInitialized) return false;", propertyName);
864                }
865            }
866
867            if (Descriptor.Proto.ExtensionRangeCount > 0)
868            {
869                writer.WriteLine("if (!ExtensionsAreInitialized) return false;");
870            }
871            writer.WriteLine("return true;");
872            writer.Outdent();
873            writer.WriteLine("}");
874            writer.Outdent();
875            writer.WriteLine("}");
876            writer.WriteLine();
877        }
878
879        internal void GenerateExtensionRegistrationCode(TextGenerator writer)
880        {
881            foreach (FieldDescriptor extension in Descriptor.Extensions)
882            {
883                new ExtensionGenerator(extension).GenerateExtensionRegistrationCode(writer);
884            }
885            foreach (MessageDescriptor nestedMessage in Descriptor.NestedTypes)
886            {
887                new MessageGenerator(nestedMessage).GenerateExtensionRegistrationCode(writer);
888            }
889        }
890    }
891}
Note: See TracBrowser for help on using the repository browser.