Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.ExtLibs/HeuristicLab.ProtobufCS/0.9.1/ProtobufCS/src/ProtoGen/MessageGenerator.cs @ 3932

Last change on this file since 3932 was 3857, checked in by abeham, 15 years ago

#866

  • Added protobuf-csharp-port project source to ExtLibs
File size: 26.4 KB
Line 
1#region Copyright notice and license
2// Protocol Buffers - Google's data interchange format
3// Copyright 2008 Google Inc.  All rights reserved.
4// http://github.com/jskeet/dotnet-protobufs/
5// Original C++/Java/Python code:
6// http://code.google.com/p/protobuf/
7//
8// Redistribution and use in source and binary forms, with or without
9// modification, are permitted provided that the following conditions are
10// met:
11//
12//     * Redistributions of source code must retain the above copyright
13// notice, this list of conditions and the following disclaimer.
14//     * Redistributions in binary form must reproduce the above
15// copyright notice, this list of conditions and the following disclaimer
16// in the documentation and/or other materials provided with the
17// distribution.
18//     * Neither the name of Google Inc. nor the names of its
19// contributors may be used to endorse or promote products derived from
20// this software without specific prior written permission.
21//
22// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33#endregion
34
35using System.Collections.Generic;
36using Google.ProtocolBuffers.DescriptorProtos;
37using Google.ProtocolBuffers.Descriptors;
38using ExtensionRange = Google.ProtocolBuffers.DescriptorProtos.DescriptorProto.Types.ExtensionRange;
39
40namespace Google.ProtocolBuffers.ProtoGen {
41  internal class MessageGenerator : SourceGeneratorBase<MessageDescriptor>, ISourceGenerator {
42    internal MessageGenerator(MessageDescriptor descriptor) : base(descriptor) {
43    }
44
45    private string ClassName {
46      get { return Descriptor.Name; }
47    }
48
49    private string FullClassName {
50      get { return GetClassName(Descriptor); }
51    }
52
53    /// <summary>
54    /// Get an identifier that uniquely identifies this type within the file.
55    /// This is used to declare static variables related to this type at the
56    /// outermost file scope.
57    /// </summary>
58    static string GetUniqueFileScopeIdentifier(IDescriptor descriptor) {
59      return "static_" + descriptor.FullName.Replace(".", "_");
60    }
61
62    internal void GenerateStaticVariables(TextGenerator writer) {
63      // Because descriptor.proto (Google.ProtocolBuffers.DescriptorProtos) is
64      // used in the construction of descriptors, we have a tricky bootstrapping
65      // problem.  To help control static initialization order, we make sure all
66      // descriptors and other static data that depends on them are members of
67      // the proto-descriptor class.  This way, they will be initialized in
68      // a deterministic order.
69
70      string identifier = GetUniqueFileScopeIdentifier(Descriptor);
71
72      // The descriptor for this type.
73      string access = Descriptor.File.CSharpOptions.NestClasses ? "private" : "internal";
74      writer.WriteLine("{0} static pbd::MessageDescriptor internal__{1}__Descriptor;", access, identifier);
75      writer.WriteLine("{0} static pb::FieldAccess.FieldAccessorTable<{1}, {1}.Builder> internal__{2}__FieldAccessorTable;",
76          access, FullClassName, identifier);
77
78      // Generate static members for all nested types.
79      foreach (MessageDescriptor nestedMessage in Descriptor.NestedTypes) {
80        new MessageGenerator(nestedMessage).GenerateStaticVariables(writer);
81      }
82    }
83
84    internal void GenerateStaticVariableInitializers(TextGenerator writer) {
85      string identifier = GetUniqueFileScopeIdentifier(Descriptor);
86
87      writer.Write("internal__{0}__Descriptor = ", identifier);
88      if (Descriptor.ContainingType == null) {
89        writer.WriteLine("Descriptor.MessageTypes[{0}];", Descriptor.Index);
90      } else {
91        writer.WriteLine("internal__{0}__Descriptor.NestedTypes[{1}];", GetUniqueFileScopeIdentifier(Descriptor.ContainingType), Descriptor.Index);
92      }
93
94      writer.WriteLine("internal__{0}__FieldAccessorTable = ", identifier);
95      writer.WriteLine("    new pb::FieldAccess.FieldAccessorTable<{1}, {1}.Builder>(internal__{0}__Descriptor,",
96          identifier, FullClassName);
97      writer.Print("        new string[] { ");
98      foreach (FieldDescriptor field in Descriptor.Fields) {
99        writer.Write("\"{0}\", ", field.CSharpOptions.PropertyName);
100      }
101      writer.WriteLine("});");
102
103      // Generate static member initializers for all nested types.
104      foreach (MessageDescriptor nestedMessage in Descriptor.NestedTypes) {
105        new MessageGenerator(nestedMessage).GenerateStaticVariableInitializers(writer);
106      }
107
108      foreach (FieldDescriptor extension in Descriptor.Extensions) {
109        new ExtensionGenerator(extension).GenerateStaticVariableInitializers(writer);
110      }
111    }
112
113    public void Generate(TextGenerator writer) {
114      writer.WriteLine("{0} sealed partial class {1} : pb::{2}Message<{1}, {1}.Builder> {{",
115          ClassAccessLevel, ClassName, Descriptor.Proto.ExtensionRangeCount > 0 ? "Extendable" : "Generated");
116      writer.Indent();
117      // Must call BuildPartial() to make sure all lists are made read-only
118      writer.WriteLine("private static readonly {0} defaultInstance = new Builder().BuildPartial();", ClassName);
119      writer.WriteLine("public static {0} DefaultInstance {{", ClassName);
120      writer.WriteLine("  get { return defaultInstance; }");
121      writer.WriteLine("}");
122      writer.WriteLine();
123      writer.WriteLine("public override {0} DefaultInstanceForType {{", ClassName);
124      writer.WriteLine("  get { return defaultInstance; }");
125      writer.WriteLine("}");
126      writer.WriteLine();
127      writer.WriteLine("protected override {0} ThisMessage {{", ClassName);
128      writer.WriteLine("  get { return this; }");
129      writer.WriteLine("}");
130      writer.WriteLine();
131      writer.WriteLine("public static pbd::MessageDescriptor Descriptor {");
132      writer.WriteLine("  get {{ return {0}.internal__{1}__Descriptor; }}", DescriptorUtil.GetFullUmbrellaClassName(Descriptor),
133          GetUniqueFileScopeIdentifier(Descriptor));
134      writer.WriteLine("}");
135      writer.WriteLine();
136      writer.WriteLine("protected override pb::FieldAccess.FieldAccessorTable<{0}, {0}.Builder> InternalFieldAccessors {{", ClassName);
137      writer.WriteLine("  get {{ return {0}.internal__{1}__FieldAccessorTable; }}", DescriptorUtil.GetFullUmbrellaClassName(Descriptor),
138          GetUniqueFileScopeIdentifier(Descriptor));
139      writer.WriteLine("}");
140      writer.WriteLine();
141
142      // Extensions don't need to go in an extra nested type
143      WriteChildren(writer, null, Descriptor.Extensions);
144
145      if (Descriptor.EnumTypes.Count + Descriptor.NestedTypes.Count > 0) {
146        writer.WriteLine("#region Nested types");
147        writer.WriteLine("public static class Types {");
148        writer.Indent();
149        WriteChildren(writer, null, Descriptor.EnumTypes);
150        WriteChildren(writer, null, Descriptor.NestedTypes);
151        writer.Outdent();
152        writer.WriteLine("}");
153        writer.WriteLine("#endregion");
154        writer.WriteLine();
155      }
156
157      foreach(FieldDescriptor fieldDescriptor in Descriptor.Fields) {
158        // Rats: we lose the debug comment here :(
159        writer.WriteLine("public const int {0} = {1};", GetFieldConstantName(fieldDescriptor), fieldDescriptor.FieldNumber);
160        SourceGenerators.CreateFieldGenerator(fieldDescriptor).GenerateMembers(writer);
161        writer.WriteLine();
162      }
163
164      if (Descriptor.File.Options.OptimizeFor == FileOptions.Types.OptimizeMode.SPEED) {
165        GenerateIsInitialized(writer);
166        GenerateMessageSerializationMethods(writer);
167      }
168
169      GenerateParseFromMethods(writer);
170      GenerateBuilder(writer);
171
172      // Force the static initialization code for the file to run, since it may
173      // initialize static variables declared in this class.
174      writer.WriteLine("static {0}() {{", ClassName);
175      // We call object.ReferenceEquals() just to make it a valid statement on its own.
176      // Another option would be GetType(), but that causes problems in DescriptorProtoFile,
177      // where the bootstrapping is somewhat recursive - type initializers call
178      // each other, effectively. We temporarily see Descriptor as null.
179      writer.WriteLine("  object.ReferenceEquals({0}.Descriptor, null);", DescriptorUtil.GetFullUmbrellaClassName(Descriptor));
180      writer.WriteLine("}");
181
182      writer.Outdent();
183      writer.WriteLine("}");
184      writer.WriteLine();
185    }
186
187    private void GenerateMessageSerializationMethods(TextGenerator writer) {
188      List<FieldDescriptor> sortedFields = new List<FieldDescriptor>(Descriptor.Fields);
189      sortedFields.Sort((f1, f2) => f1.FieldNumber.CompareTo(f2.FieldNumber));
190
191      List<ExtensionRange> sortedExtensions = new List<ExtensionRange>(Descriptor.Proto.ExtensionRangeList);
192      sortedExtensions.Sort((r1, r2) => (r1.Start.CompareTo(r2.Start)));
193
194      writer.WriteLine("public override void WriteTo(pb::CodedOutputStream output) {");
195      writer.Indent();
196      // Make sure we've computed the serialized length, so that packed fields are generated correctly.
197      writer.WriteLine("int size = SerializedSize;");
198      if (Descriptor.Proto.ExtensionRangeList.Count > 0) {
199        writer.WriteLine("pb::ExtendableMessage<{0}, {0}.Builder>.ExtensionWriter extensionWriter = CreateExtensionWriter(this);",
200          ClassName);
201      }
202
203      // Merge the fields and the extension ranges, both sorted by field number.
204      for (int i = 0, j = 0; i < Descriptor.Fields.Count || j < sortedExtensions.Count; ) {
205        if (i == Descriptor.Fields.Count) {
206          GenerateSerializeOneExtensionRange(writer, sortedExtensions[j++]);
207        } else if (j == sortedExtensions.Count) {
208          GenerateSerializeOneField(writer, sortedFields[i++]);
209        } else if (sortedFields[i].FieldNumber < sortedExtensions[j].Start) {
210          GenerateSerializeOneField(writer, sortedFields[i++]);
211        } else {
212          GenerateSerializeOneExtensionRange(writer, sortedExtensions[j++]);
213        }
214      }
215
216      if (Descriptor.Proto.Options.MessageSetWireFormat) {
217        writer.WriteLine("UnknownFields.WriteAsMessageSetTo(output);");
218      } else {
219        writer.WriteLine("UnknownFields.WriteTo(output);");
220      }
221
222      writer.Outdent();
223      writer.WriteLine("}");
224      writer.WriteLine();
225      writer.WriteLine("private int memoizedSerializedSize = -1;");
226      writer.WriteLine("public override int SerializedSize {");
227      writer.Indent();
228      writer.WriteLine("get {");
229      writer.Indent();
230      writer.WriteLine("int size = memoizedSerializedSize;");
231      writer.WriteLine("if (size != -1) return size;");
232      writer.WriteLine();
233      writer.WriteLine("size = 0;");
234      foreach (FieldDescriptor field in Descriptor.Fields) {
235        SourceGenerators.CreateFieldGenerator(field).GenerateSerializedSizeCode(writer);
236      }
237      if (Descriptor.Proto.ExtensionRangeCount > 0) {
238        writer.WriteLine("size += ExtensionsSerializedSize;");
239      }
240
241      if (Descriptor.Options.MessageSetWireFormat) {
242        writer.WriteLine("size += UnknownFields.SerializedSizeAsMessageSet;");
243      } else {
244        writer.WriteLine("size += UnknownFields.SerializedSize;");
245      }
246      writer.WriteLine("memoizedSerializedSize = size;");
247      writer.WriteLine("return size;");
248      writer.Outdent();
249      writer.WriteLine("}");
250      writer.Outdent();
251      writer.WriteLine("}");
252      writer.WriteLine();
253    }
254
255    private static void GenerateSerializeOneField(TextGenerator writer, FieldDescriptor fieldDescriptor) {
256      SourceGenerators.CreateFieldGenerator(fieldDescriptor).GenerateSerializationCode(writer);
257    }
258
259    private static void GenerateSerializeOneExtensionRange(TextGenerator writer, ExtensionRange extensionRange) {
260      writer.WriteLine("extensionWriter.WriteUntil({0}, output);", extensionRange.End);
261    }
262
263    private void GenerateParseFromMethods(TextGenerator writer) {
264      // Note:  These are separate from GenerateMessageSerializationMethods()
265      //   because they need to be generated even for messages that are optimized
266      //   for code size.
267
268      writer.WriteLine("public static {0} ParseFrom(pb::ByteString data) {{", ClassName);
269      writer.WriteLine("  return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed();");
270      writer.WriteLine("}");
271      writer.WriteLine("public static {0} ParseFrom(pb::ByteString data, pb::ExtensionRegistry extensionRegistry) {{", ClassName);
272      writer.WriteLine("  return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();");
273      writer.WriteLine("}");
274      writer.WriteLine("public static {0} ParseFrom(byte[] data) {{", ClassName);
275      writer.WriteLine("  return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed();");
276      writer.WriteLine("}");
277      writer.WriteLine("public static {0} ParseFrom(byte[] data, pb::ExtensionRegistry extensionRegistry) {{", ClassName);
278      writer.WriteLine("  return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();");
279      writer.WriteLine("}");
280      writer.WriteLine("public static {0} ParseFrom(global::System.IO.Stream input) {{", ClassName);
281      writer.WriteLine("  return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed();");
282      writer.WriteLine("}");
283      writer.WriteLine("public static {0} ParseFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) {{", ClassName);
284      writer.WriteLine("  return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();");
285      writer.WriteLine("}");
286      writer.WriteLine("public static {0} ParseDelimitedFrom(global::System.IO.Stream input) {{", ClassName);
287      writer.WriteLine("  return CreateBuilder().MergeDelimitedFrom(input).BuildParsed();");
288      writer.WriteLine("}");
289      writer.WriteLine("public static {0} ParseDelimitedFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) {{", ClassName);
290      writer.WriteLine("  return CreateBuilder().MergeDelimitedFrom(input, extensionRegistry).BuildParsed();");
291      writer.WriteLine("}");
292      writer.WriteLine("public static {0} ParseFrom(pb::CodedInputStream input) {{", ClassName);
293      writer.WriteLine("  return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed();");
294      writer.WriteLine("}");
295      writer.WriteLine("public static {0} ParseFrom(pb::CodedInputStream input, pb::ExtensionRegistry extensionRegistry) {{", ClassName);
296      writer.WriteLine("  return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();");
297      writer.WriteLine("}");
298    }
299
300    /// <summary>
301    /// Returns whether or not the specified message type has any required fields.
302    /// If it doesn't, calls to check for initialization can be optimised.
303    /// TODO(jonskeet): Move this into MessageDescriptor?
304    /// </summary>
305    private static bool HasRequiredFields(MessageDescriptor descriptor, Dictionary<MessageDescriptor,object> alreadySeen) {
306      if (alreadySeen.ContainsKey(descriptor)) {
307        // The type is already in cache.  This means that either:
308        // a. The type has no required fields.
309        // b. We are in the midst of checking if the type has required fields,
310        //    somewhere up the stack.  In this case, we know that if the type
311        //    has any required fields, they'll be found when we return to it,
312        //    and the whole call to HasRequiredFields() will return true.
313        //    Therefore, we don't have to check if this type has required fields
314        //    here.
315        return false;
316      }
317      alreadySeen[descriptor] = descriptor; // Value is irrelevant
318
319      // If the type has extensions, an extension with message type could contain
320      // required fields, so we have to be conservative and assume such an
321      // extension exists.
322      if (descriptor.Extensions.Count > 0) {
323        return true;
324      }
325
326      foreach (FieldDescriptor field in descriptor.Fields) {
327        if (field.IsRequired) {
328          return true;
329        }
330        // Message or group
331        if (field.MappedType == MappedType.Message) {
332          if (HasRequiredFields(field.MessageType, alreadySeen)) {
333            return true;
334          }
335        }
336      }
337      return false;
338    }
339
340    private void GenerateBuilder(TextGenerator writer) {
341      writer.WriteLine("public static Builder CreateBuilder() { return new Builder(); }");
342      writer.WriteLine("public override Builder ToBuilder() { return CreateBuilder(this); }");
343      writer.WriteLine("public override Builder CreateBuilderForType() { return new Builder(); }");
344      writer.WriteLine("public static Builder CreateBuilder({0} prototype) {{", ClassName);
345      writer.WriteLine("  return (Builder) new Builder().MergeFrom(prototype);");
346      writer.WriteLine("}");
347      writer.WriteLine();
348      writer.WriteLine("{0} sealed partial class Builder : pb::{2}Builder<{1}, Builder> {{",
349          ClassAccessLevel, ClassName, Descriptor.Proto.ExtensionRangeCount > 0 ? "Extendable" : "Generated");
350      writer.Indent();
351      writer.WriteLine("protected override Builder ThisBuilder {");
352      writer.WriteLine("  get { return this; }");
353      writer.WriteLine("}");
354      GenerateCommonBuilderMethods(writer);
355      if (Descriptor.File.Options.OptimizeFor == FileOptions.Types.OptimizeMode.SPEED) {
356        GenerateBuilderParsingMethods(writer);
357      }
358      foreach (FieldDescriptor field in Descriptor.Fields) {
359        writer.WriteLine();
360        // No field comment :(
361        SourceGenerators.CreateFieldGenerator(field).GenerateBuilderMembers(writer);
362      }
363      writer.Outdent();
364      writer.WriteLine("}");
365    }
366
367    private void GenerateCommonBuilderMethods(TextGenerator writer) {
368      writer.WriteLine("{0} Builder() {{}}", ClassAccessLevel);
369      writer.WriteLine();
370      writer.WriteLine("{0} result = new {0}();", ClassName);
371      writer.WriteLine();
372      writer.WriteLine("protected override {0} MessageBeingBuilt {{", ClassName);
373      writer.WriteLine("  get { return result; }");
374      writer.WriteLine("}");
375      writer.WriteLine();
376      writer.WriteLine("public override Builder Clear() {");
377      writer.WriteLine("  result = new {0}();", ClassName);
378      writer.WriteLine("  return this;");
379      writer.WriteLine("}");
380      writer.WriteLine();
381      writer.WriteLine("public override Builder Clone() {");
382      writer.WriteLine("  return new Builder().MergeFrom(result);");
383      writer.WriteLine("}");
384      writer.WriteLine();
385      writer.WriteLine("public override pbd::MessageDescriptor DescriptorForType {");
386      writer.WriteLine("  get {{ return {0}.Descriptor; }}", FullClassName);
387      writer.WriteLine("}");
388      writer.WriteLine();
389      writer.WriteLine("public override {0} DefaultInstanceForType {{", ClassName);
390      writer.WriteLine("  get {{ return {0}.DefaultInstance; }}", FullClassName);
391      writer.WriteLine("}");
392      writer.WriteLine();
393   
394      writer.WriteLine("public override {0} BuildPartial() {{", ClassName);
395      writer.Indent();
396      writer.WriteLine("if (result == null) {");
397      writer.WriteLine("  throw new global::System.InvalidOperationException(\"build() has already been called on this Builder\");");
398      writer.WriteLine("}");
399      foreach (FieldDescriptor field in Descriptor.Fields) {
400        SourceGenerators.CreateFieldGenerator(field).GenerateBuildingCode(writer);
401      }
402      writer.WriteLine("{0} returnMe = result;", ClassName);
403      writer.WriteLine("result = null;");
404      writer.WriteLine("return returnMe;");
405      writer.Outdent();
406      writer.WriteLine("}");
407      writer.WriteLine();
408
409      if (Descriptor.File.Options.OptimizeFor == FileOptions.Types.OptimizeMode.SPEED) {
410        writer.WriteLine("public override Builder MergeFrom(pb::IMessage other) {");
411        writer.WriteLine("  if (other is {0}) {{", ClassName);
412        writer.WriteLine("    return MergeFrom(({0}) other);", ClassName);
413        writer.WriteLine("  } else {");
414        writer.WriteLine("    base.MergeFrom(other);");
415        writer.WriteLine("    return this;");
416        writer.WriteLine("  }");
417        writer.WriteLine("}");
418        writer.WriteLine();
419        writer.WriteLine("public override Builder MergeFrom({0} other) {{", ClassName);
420        // Optimization:  If other is the default instance, we know none of its
421        // fields are set so we can skip the merge.
422        writer.Indent();
423        writer.WriteLine("if (other == {0}.DefaultInstance) return this;", FullClassName);
424        foreach (FieldDescriptor field in Descriptor.Fields) {
425          SourceGenerators.CreateFieldGenerator(field).GenerateMergingCode(writer);
426        }
427        // if message type has extensions
428        if (Descriptor.Proto.ExtensionRangeCount > 0) {
429          writer.WriteLine("  this.MergeExtensionFields(other);");
430        }
431        writer.WriteLine("this.MergeUnknownFields(other.UnknownFields);");
432        writer.WriteLine("return this;");
433        writer.Outdent();
434        writer.WriteLine("}");
435        writer.WriteLine();
436      }
437    }
438
439    private void GenerateBuilderParsingMethods(TextGenerator writer) {
440      List<FieldDescriptor> sortedFields = new List<FieldDescriptor>(Descriptor.Fields);
441      sortedFields.Sort((f1, f2) => f1.FieldNumber.CompareTo(f2.FieldNumber));
442
443      writer.WriteLine("public override Builder MergeFrom(pb::CodedInputStream input) {");
444      writer.WriteLine("  return MergeFrom(input, pb::ExtensionRegistry.Empty);");
445      writer.WriteLine("}");
446      writer.WriteLine();
447      writer.WriteLine("public override Builder MergeFrom(pb::CodedInputStream input, pb::ExtensionRegistry extensionRegistry) {");
448      writer.Indent();
449      writer.WriteLine("pb::UnknownFieldSet.Builder unknownFields = null;");
450      writer.WriteLine("while (true) {");
451      writer.Indent();
452      writer.WriteLine("uint tag = input.ReadTag();");
453      writer.WriteLine("switch (tag) {");
454      writer.Indent();
455      writer.WriteLine("case 0: {"); // 0 signals EOF / limit reached
456      writer.WriteLine("  if (unknownFields != null) {");
457      writer.WriteLine("    this.UnknownFields = unknownFields.Build();");
458      writer.WriteLine("  }");
459      writer.WriteLine("  return this;");
460      writer.WriteLine("}");
461      writer.WriteLine("default: {");
462      writer.WriteLine("  if (pb::WireFormat.IsEndGroupTag(tag)) {");
463      writer.WriteLine("    if (unknownFields != null) {");
464      writer.WriteLine("      this.UnknownFields = unknownFields.Build();");
465      writer.WriteLine("    }");
466      writer.WriteLine("    return this;"); // it's an endgroup tag
467      writer.WriteLine("  }");
468      writer.WriteLine("  if (unknownFields == null) {"); // First unknown field - create builder now
469      writer.WriteLine("    unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields);");
470      writer.WriteLine("  }");
471      writer.WriteLine("  ParseUnknownField(input, unknownFields, extensionRegistry, tag);");
472      writer.WriteLine("  break;");
473      writer.WriteLine("}");
474      foreach (FieldDescriptor field in sortedFields) {
475        uint tag = WireFormat.MakeTag(field);
476        writer.WriteLine("case {0}: {{", tag);
477        writer.Indent();
478        SourceGenerators.CreateFieldGenerator(field).GenerateParsingCode(writer);
479        writer.WriteLine("break;");
480        writer.Outdent();
481        writer.WriteLine("}");
482      }
483      writer.Outdent();
484      writer.WriteLine("}");
485      writer.Outdent();
486      writer.WriteLine("}");
487      writer.Outdent();
488      writer.WriteLine("}");
489      writer.WriteLine();
490    }
491
492    private void GenerateIsInitialized(TextGenerator writer) {
493      writer.WriteLine("public override bool IsInitialized {");
494      writer.Indent();
495      writer.WriteLine("get {");
496      writer.Indent();
497
498      // Check that all required fields in this message are set.
499      // TODO(kenton):  We can optimize this when we switch to putting all the
500      // "has" fields into a single bitfield.
501      foreach (FieldDescriptor field in Descriptor.Fields) {
502        if (field.IsRequired) {
503          writer.WriteLine("if (!has{0}) return false;", field.CSharpOptions.PropertyName);
504        }
505      }
506 
507      // Now check that all embedded messages are initialized.
508      foreach (FieldDescriptor field in Descriptor.Fields) {
509        if (field.FieldType != FieldType.Message ||
510            !HasRequiredFields(field.MessageType, new Dictionary<MessageDescriptor, object>())) {
511          continue;
512        }
513        string propertyName = NameHelpers.UnderscoresToPascalCase(GetFieldName(field));
514        if (field.IsRepeated) {
515          writer.WriteLine("foreach ({0} element in {1}List) {{", GetClassName(field.MessageType), propertyName);
516          writer.WriteLine("  if (!element.IsInitialized) return false;");
517          writer.WriteLine("}");
518        } else if (field.IsOptional) {
519          writer.WriteLine("if (Has{0}) {{", propertyName);
520          writer.WriteLine("  if (!{0}.IsInitialized) return false;", propertyName);
521          writer.WriteLine("}");
522        } else {
523          writer.WriteLine("if (!{0}.IsInitialized) return false;", propertyName);
524        }
525      }
526
527      if (Descriptor.Proto.ExtensionRangeCount > 0) {
528        writer.WriteLine("if (!ExtensionsAreInitialized) return false;");
529      }
530      writer.WriteLine("return true;");
531      writer.Outdent();
532      writer.WriteLine("}");
533      writer.Outdent();
534      writer.WriteLine("}");
535      writer.WriteLine();
536    }
537
538    internal void GenerateExtensionRegistrationCode(TextGenerator writer) {
539      foreach (FieldDescriptor extension in Descriptor.Extensions) {
540        new ExtensionGenerator(extension).GenerateExtensionRegistrationCode(writer);
541      }
542      foreach (MessageDescriptor nestedMessage in Descriptor.NestedTypes) {
543        new MessageGenerator(nestedMessage).GenerateExtensionRegistrationCode(writer);
544      }
545    }
546  }
547}
Note: See TracBrowser for help on using the repository browser.