blob: 6dc786c1684c6f04e574ff1a3ea9b962444e344c [file] [log] [blame]
Jon Skeet60c059b2008-10-23 21:17:56 +01001using System.Collections;
Jon Skeet68036862008-10-22 13:30:34 +01002using Google.ProtocolBuffers.Descriptors;
3using Google.ProtocolBuffers.DescriptorProtos;
4using System.Collections.Generic;
5
6using ExtensionRange = Google.ProtocolBuffers.DescriptorProtos.DescriptorProto.Types.ExtensionRange;
7
8namespace Google.ProtocolBuffers.ProtoGen {
9 internal class MessageGenerator : SourceGeneratorBase<MessageDescriptor>, ISourceGenerator {
10 internal MessageGenerator(MessageDescriptor descriptor) : base(descriptor) {
11 }
12
13 private string ClassName {
14 get { return Descriptor.Name; }
15 }
16
17 private string FullClassName {
Jon Skeetd6343be2008-11-12 23:39:44 +000018 get { return GetClassName(Descriptor); }
Jon Skeet68036862008-10-22 13:30:34 +010019 }
20
21 /// <summary>
22 /// Get an identifier that uniquely identifies this type within the file.
23 /// This is used to declare static variables related to this type at the
24 /// outermost file scope.
25 /// </summary>
26 static string GetUniqueFileScopeIdentifier(IDescriptor descriptor) {
27 return "static_" + descriptor.FullName.Replace(".", "_");
28 }
29
30 internal void GenerateStaticVariables(TextGenerator writer) {
31 // Because descriptor.proto (Google.ProtocolBuffers.DescriptorProtos) is
32 // used in the construction of descriptors, we have a tricky bootstrapping
33 // problem. To help control static initialization order, we make sure all
34 // descriptors and other static data that depends on them are members of
35 // the proto-descriptor class. This way, they will be initialized in
36 // a deterministic order.
37
38 string identifier = GetUniqueFileScopeIdentifier(Descriptor);
39
40 // The descriptor for this type.
Jon Skeetd6343be2008-11-12 23:39:44 +000041 string access = Descriptor.File.CSharpOptions.NestClasses ? "private" : "internal";
Jon Skeetdf67f142009-06-05 19:29:36 +010042 writer.WriteLine("{0} static pbd::MessageDescriptor internal__{1}__Descriptor;", access, identifier);
43 writer.WriteLine("{0} static pb::FieldAccess.FieldAccessorTable<{1}, {1}.Builder> internal__{2}__FieldAccessorTable;",
Jon Skeet68036862008-10-22 13:30:34 +010044 access, FullClassName, identifier);
Jon Skeetdf67f142009-06-05 19:29:36 +010045
46 // Generate static members for all nested types.
47 foreach (MessageDescriptor nestedMessage in Descriptor.NestedTypes) {
48 new MessageGenerator(nestedMessage).GenerateStaticVariables(writer);
49 }
50 }
51
52 internal void GenerateStaticVariableInitializers(TextGenerator writer) {
53 string identifier = GetUniqueFileScopeIdentifier(Descriptor);
54
55 writer.Write("internal__{0}__Descriptor = ", identifier);
56 if (Descriptor.ContainingType == null) {
57 writer.WriteLine("Descriptor.MessageTypes[{0}];", Descriptor.Index);
58 } else {
59 writer.WriteLine("internal__{0}__Descriptor.NestedTypes[{1}];", GetUniqueFileScopeIdentifier(Descriptor.ContainingType), Descriptor.Index);
60 }
61
62 writer.WriteLine("internal__{0}__FieldAccessorTable = ", identifier);
63 writer.WriteLine(" new pb::FieldAccess.FieldAccessorTable<{1}, {1}.Builder>(internal__{0}__Descriptor,",
64 identifier, FullClassName);
Jon Skeet68036862008-10-22 13:30:34 +010065 writer.Print(" new string[] { ");
66 foreach (FieldDescriptor field in Descriptor.Fields) {
Jon Skeet4cf9e3c2008-11-24 11:11:28 +000067 writer.Write("\"{0}\", ", field.CSharpOptions.PropertyName);
Jon Skeet68036862008-10-22 13:30:34 +010068 }
69 writer.WriteLine("});");
70
Jon Skeetdf67f142009-06-05 19:29:36 +010071 // Generate static member initializers for all nested types.
Jon Skeet68036862008-10-22 13:30:34 +010072 foreach (MessageDescriptor nestedMessage in Descriptor.NestedTypes) {
Jon Skeetdf67f142009-06-05 19:29:36 +010073 new MessageGenerator(nestedMessage).GenerateStaticVariableInitializers(writer);
74 }
75
76 foreach (FieldDescriptor extension in Descriptor.Extensions) {
77 new ExtensionGenerator(extension).GenerateStaticVariableInitializers(writer);
Jon Skeet68036862008-10-22 13:30:34 +010078 }
79 }
80
81 public void Generate(TextGenerator writer) {
82 writer.WriteLine("{0} sealed partial class {1} : pb::{2}Message<{1}, {1}.Builder> {{",
83 ClassAccessLevel, ClassName, Descriptor.Proto.ExtensionRangeCount > 0 ? "Extendable" : "Generated");
84 writer.Indent();
85 // Must call BuildPartial() to make sure all lists are made read-only
86 writer.WriteLine("private static readonly {0} defaultInstance = new Builder().BuildPartial();", ClassName);
87 writer.WriteLine("public static {0} DefaultInstance {{", ClassName);
88 writer.WriteLine(" get { return defaultInstance; }");
89 writer.WriteLine("}");
90 writer.WriteLine();
91 writer.WriteLine("public override {0} DefaultInstanceForType {{", ClassName);
92 writer.WriteLine(" get { return defaultInstance; }");
93 writer.WriteLine("}");
94 writer.WriteLine();
95 writer.WriteLine("protected override {0} ThisMessage {{", ClassName);
96 writer.WriteLine(" get { return this; }");
97 writer.WriteLine("}");
98 writer.WriteLine();
99 writer.WriteLine("public static pbd::MessageDescriptor Descriptor {");
Jon Skeetd6343be2008-11-12 23:39:44 +0000100 writer.WriteLine(" get {{ return {0}.internal__{1}__Descriptor; }}", DescriptorUtil.GetFullUmbrellaClassName(Descriptor),
Jon Skeet68036862008-10-22 13:30:34 +0100101 GetUniqueFileScopeIdentifier(Descriptor));
102 writer.WriteLine("}");
103 writer.WriteLine();
104 writer.WriteLine("protected override pb::FieldAccess.FieldAccessorTable<{0}, {0}.Builder> InternalFieldAccessors {{", ClassName);
Jon Skeetd6343be2008-11-12 23:39:44 +0000105 writer.WriteLine(" get {{ return {0}.internal__{1}__FieldAccessorTable; }}", DescriptorUtil.GetFullUmbrellaClassName(Descriptor),
Jon Skeet68036862008-10-22 13:30:34 +0100106 GetUniqueFileScopeIdentifier(Descriptor));
107 writer.WriteLine("}");
108 writer.WriteLine();
109
110 // Extensions don't need to go in an extra nested type
111 WriteChildren(writer, null, Descriptor.Extensions);
112
113 if (Descriptor.EnumTypes.Count + Descriptor.NestedTypes.Count > 0) {
114 writer.WriteLine("#region Nested types");
115 writer.WriteLine("public static class Types {");
116 writer.Indent();
117 WriteChildren(writer, null, Descriptor.EnumTypes);
118 WriteChildren(writer, null, Descriptor.NestedTypes);
119 writer.Outdent();
120 writer.WriteLine("}");
121 writer.WriteLine("#endregion");
122 writer.WriteLine();
123 }
124
125 foreach(FieldDescriptor fieldDescriptor in Descriptor.Fields) {
126 // Rats: we lose the debug comment here :(
Jon Skeet7ee85c42009-05-28 21:11:15 +0100127 writer.WriteLine("public const int {0} = {1};", GetFieldConstantName(fieldDescriptor), fieldDescriptor.FieldNumber);
Jon Skeet68036862008-10-22 13:30:34 +0100128 SourceGenerators.CreateFieldGenerator(fieldDescriptor).GenerateMembers(writer);
129 writer.WriteLine();
130 }
131
132 if (Descriptor.File.Options.OptimizeFor == FileOptions.Types.OptimizeMode.SPEED) {
133 GenerateIsInitialized(writer);
134 GenerateMessageSerializationMethods(writer);
135 }
136
137 GenerateParseFromMethods(writer);
138 GenerateBuilder(writer);
Jon Skeetdf67f142009-06-05 19:29:36 +0100139
140 // Force the static initialization code for the file to run, since it may
141 // initialize static variables declared in this class.
142 writer.WriteLine("static {0}() {{", ClassName);
Jon Skeetc784be32009-06-17 15:47:33 +0100143 // We call object.ReferenceEquals() just to make it a valid statement on its own.
144 // Another option would be GetType(), but that causes problems in DescriptorProtoFile,
145 // where the bootstrapping is somewhat recursive - type initializers call
146 // each other, effectively. We temporarily see Descriptor as null.
147 writer.WriteLine(" object.ReferenceEquals({0}.Descriptor, null);", DescriptorUtil.GetFullUmbrellaClassName(Descriptor));
Jon Skeetdf67f142009-06-05 19:29:36 +0100148 writer.WriteLine("}");
149
150 writer.Outdent();
151 writer.WriteLine("}");
152 writer.WriteLine();
Jon Skeet68036862008-10-22 13:30:34 +0100153 }
154
155 private void GenerateMessageSerializationMethods(TextGenerator writer) {
156 List<FieldDescriptor> sortedFields = new List<FieldDescriptor>(Descriptor.Fields);
157 sortedFields.Sort((f1, f2) => f1.FieldNumber.CompareTo(f2.FieldNumber));
158
159 List<ExtensionRange> sortedExtensions = new List<ExtensionRange>(Descriptor.Proto.ExtensionRangeList);
160 sortedExtensions.Sort((r1, r2) => (r1.Start.CompareTo(r2.Start)));
161
162 writer.WriteLine("public override void WriteTo(pb::CodedOutputStream output) {");
163 writer.Indent();
164 if (Descriptor.Proto.ExtensionRangeList.Count > 0) {
165 writer.WriteLine("pb::ExtendableMessage<{0}, {0}.Builder>.ExtensionWriter extensionWriter = CreateExtensionWriter(this);",
166 ClassName);
167 }
168
169 // Merge the fields and the extension ranges, both sorted by field number.
170 for (int i = 0, j = 0; i < Descriptor.Fields.Count || j < sortedExtensions.Count; ) {
171 if (i == Descriptor.Fields.Count) {
172 GenerateSerializeOneExtensionRange(writer, sortedExtensions[j++]);
173 } else if (j == sortedExtensions.Count) {
174 GenerateSerializeOneField(writer, sortedFields[i++]);
175 } else if (sortedFields[i].FieldNumber < sortedExtensions[j].Start) {
176 GenerateSerializeOneField(writer, sortedFields[i++]);
177 } else {
178 GenerateSerializeOneExtensionRange(writer, sortedExtensions[j++]);
179 }
180 }
181
182 if (Descriptor.Proto.Options.MessageSetWireFormat) {
183 writer.WriteLine("UnknownFields.WriteAsMessageSetTo(output);");
184 } else {
185 writer.WriteLine("UnknownFields.WriteTo(output);");
186 }
187
188 writer.Outdent();
189 writer.WriteLine("}");
190 writer.WriteLine();
191 writer.WriteLine("private int memoizedSerializedSize = -1;");
192 writer.WriteLine("public override int SerializedSize {");
193 writer.Indent();
194 writer.WriteLine("get {");
195 writer.Indent();
196 writer.WriteLine("int size = memoizedSerializedSize;");
197 writer.WriteLine("if (size != -1) return size;");
198 writer.WriteLine();
199 writer.WriteLine("size = 0;");
200 foreach (FieldDescriptor field in Descriptor.Fields) {
201 SourceGenerators.CreateFieldGenerator(field).GenerateSerializedSizeCode(writer);
202 }
203 if (Descriptor.Proto.ExtensionRangeCount > 0) {
204 writer.WriteLine("size += ExtensionsSerializedSize;");
205 }
206
207 if (Descriptor.Options.MessageSetWireFormat) {
208 writer.WriteLine("size += UnknownFields.SerializedSizeAsMessageSet;");
209 } else {
210 writer.WriteLine("size += UnknownFields.SerializedSize;");
211 }
212 writer.WriteLine("memoizedSerializedSize = size;");
213 writer.WriteLine("return size;");
214 writer.Outdent();
215 writer.WriteLine("}");
216 writer.Outdent();
217 writer.WriteLine("}");
218 writer.WriteLine();
219 }
220
Jon Skeet7ee85c42009-05-28 21:11:15 +0100221 private static void GenerateSerializeOneField(TextGenerator writer, FieldDescriptor fieldDescriptor) {
Jon Skeet68036862008-10-22 13:30:34 +0100222 SourceGenerators.CreateFieldGenerator(fieldDescriptor).GenerateSerializationCode(writer);
223 }
224
225 private static void GenerateSerializeOneExtensionRange(TextGenerator writer, ExtensionRange extensionRange) {
226 writer.WriteLine("extensionWriter.WriteUntil({0}, output);", extensionRange.End);
227 }
228
229 private void GenerateParseFromMethods(TextGenerator writer) {
230 // Note: These are separate from GenerateMessageSerializationMethods()
231 // because they need to be generated even for messages that are optimized
232 // for code size.
233
234 writer.WriteLine("public static {0} ParseFrom(pb::ByteString data) {{", ClassName);
235 writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed();");
236 writer.WriteLine("}");
237 writer.WriteLine("public static {0} ParseFrom(pb::ByteString data, pb::ExtensionRegistry extensionRegistry) {{", ClassName);
238 writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();");
239 writer.WriteLine("}");
240 writer.WriteLine("public static {0} ParseFrom(byte[] data) {{", ClassName);
241 writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed();");
242 writer.WriteLine("}");
243 writer.WriteLine("public static {0} ParseFrom(byte[] data, pb::ExtensionRegistry extensionRegistry) {{", ClassName);
244 writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();");
245 writer.WriteLine("}");
246 writer.WriteLine("public static {0} ParseFrom(global::System.IO.Stream input) {{", ClassName);
247 writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed();");
248 writer.WriteLine("}");
249 writer.WriteLine("public static {0} ParseFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) {{", ClassName);
250 writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();");
251 writer.WriteLine("}");
Jon Skeet2e6dc122009-05-29 06:34:52 +0100252 writer.WriteLine("public static {0} ParseDelimitedFrom(global::System.IO.Stream input) {{", ClassName);
253 writer.WriteLine(" return CreateBuilder().MergeDelimitedFrom(input).BuildParsed();");
254 writer.WriteLine("}");
255 writer.WriteLine("public static {0} ParseDelimitedFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) {{", ClassName);
256 writer.WriteLine(" return CreateBuilder().MergeDelimitedFrom(input, extensionRegistry).BuildParsed();");
257 writer.WriteLine("}");
Jon Skeet68036862008-10-22 13:30:34 +0100258 writer.WriteLine("public static {0} ParseFrom(pb::CodedInputStream input) {{", ClassName);
259 writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed();");
260 writer.WriteLine("}");
261 writer.WriteLine("public static {0} ParseFrom(pb::CodedInputStream input, pb::ExtensionRegistry extensionRegistry) {{", ClassName);
262 writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();");
263 writer.WriteLine("}");
264 }
265
266 /// <summary>
267 /// Returns whether or not the specified message type has any required fields.
268 /// If it doesn't, calls to check for initialization can be optimised.
269 /// TODO(jonskeet): Move this into MessageDescriptor?
270 /// </summary>
271 private static bool HasRequiredFields(MessageDescriptor descriptor, Dictionary<MessageDescriptor,object> alreadySeen) {
272 if (alreadySeen.ContainsKey(descriptor)) {
273 // The type is already in cache. This means that either:
274 // a. The type has no required fields.
275 // b. We are in the midst of checking if the type has required fields,
276 // somewhere up the stack. In this case, we know that if the type
277 // has any required fields, they'll be found when we return to it,
278 // and the whole call to HasRequiredFields() will return true.
279 // Therefore, we don't have to check if this type has required fields
280 // here.
281 return false;
282 }
283 alreadySeen[descriptor] = descriptor; // Value is irrelevant
284
285 // If the type has extensions, an extension with message type could contain
286 // required fields, so we have to be conservative and assume such an
287 // extension exists.
288 if (descriptor.Extensions.Count > 0) {
289 return true;
290 }
291
292 foreach (FieldDescriptor field in descriptor.Fields) {
293 if (field.IsRequired) {
294 return true;
295 }
296 // Message or group
297 if (field.MappedType == MappedType.Message) {
298 if (HasRequiredFields(field.MessageType, alreadySeen)) {
299 return true;
300 }
301 }
302 }
303 return false;
304 }
305
306 private void GenerateBuilder(TextGenerator writer) {
307 writer.WriteLine("public static Builder CreateBuilder() { return new Builder(); }");
Jon Skeete81a9d72009-02-24 16:50:56 +0000308 writer.WriteLine("public override Builder ToBuilder() { return CreateBuilder(this); }");
Jon Skeet68036862008-10-22 13:30:34 +0100309 writer.WriteLine("public override Builder CreateBuilderForType() { return new Builder(); }");
310 writer.WriteLine("public static Builder CreateBuilder({0} prototype) {{", ClassName);
311 writer.WriteLine(" return (Builder) new Builder().MergeFrom(prototype);");
312 writer.WriteLine("}");
313 writer.WriteLine();
314 writer.WriteLine("{0} sealed partial class Builder : pb::{2}Builder<{1}, Builder> {{",
315 ClassAccessLevel, ClassName, Descriptor.Proto.ExtensionRangeCount > 0 ? "Extendable" : "Generated");
316 writer.Indent();
317 writer.WriteLine("protected override Builder ThisBuilder {");
318 writer.WriteLine(" get { return this; }");
319 writer.WriteLine("}");
320 GenerateCommonBuilderMethods(writer);
321 if (Descriptor.File.Options.OptimizeFor == FileOptions.Types.OptimizeMode.SPEED) {
322 GenerateBuilderParsingMethods(writer);
323 }
324 foreach (FieldDescriptor field in Descriptor.Fields) {
325 writer.WriteLine();
326 // No field comment :(
327 SourceGenerators.CreateFieldGenerator(field).GenerateBuilderMembers(writer);
328 }
329 writer.Outdent();
330 writer.WriteLine("}");
Jon Skeet68036862008-10-22 13:30:34 +0100331 }
332
333 private void GenerateCommonBuilderMethods(TextGenerator writer) {
334 writer.WriteLine("{0} Builder() {{}}", ClassAccessLevel);
335 writer.WriteLine();
336 writer.WriteLine("{0} result = new {0}();", ClassName);
337 writer.WriteLine();
338 writer.WriteLine("protected override {0} MessageBeingBuilt {{", ClassName);
339 writer.WriteLine(" get { return result; }");
340 writer.WriteLine("}");
341 writer.WriteLine();
342 writer.WriteLine("public override Builder Clear() {");
343 writer.WriteLine(" result = new {0}();", ClassName);
344 writer.WriteLine(" return this;");
345 writer.WriteLine("}");
346 writer.WriteLine();
347 writer.WriteLine("public override Builder Clone() {");
348 writer.WriteLine(" return new Builder().MergeFrom(result);");
349 writer.WriteLine("}");
350 writer.WriteLine();
351 writer.WriteLine("public override pbd::MessageDescriptor DescriptorForType {");
352 writer.WriteLine(" get {{ return {0}.Descriptor; }}", ClassName);
353 writer.WriteLine("}");
354 writer.WriteLine();
355 writer.WriteLine("public override {0} DefaultInstanceForType {{", ClassName);
356 writer.WriteLine(" get {{ return {0}.DefaultInstance; }}", ClassName);
357 writer.WriteLine("}");
358 writer.WriteLine();
359
360 writer.WriteLine("public override {0} BuildPartial() {{", ClassName);
361 writer.Indent();
Jon Skeet2e6dc122009-05-29 06:34:52 +0100362 writer.WriteLine("if (result == null) {");
363 writer.WriteLine(" throw new global::System.InvalidOperationException(\"build() has already been called on this Builder\");");
364 writer.WriteLine("}");
Jon Skeet68036862008-10-22 13:30:34 +0100365 foreach (FieldDescriptor field in Descriptor.Fields) {
366 SourceGenerators.CreateFieldGenerator(field).GenerateBuildingCode(writer);
367 }
368 writer.WriteLine("{0} returnMe = result;", ClassName);
369 writer.WriteLine("result = null;");
370 writer.WriteLine("return returnMe;");
371 writer.Outdent();
372 writer.WriteLine("}");
373 writer.WriteLine();
374
375 if (Descriptor.File.Options.OptimizeFor == FileOptions.Types.OptimizeMode.SPEED) {
376 writer.WriteLine("public override Builder MergeFrom(pb::IMessage other) {");
377 writer.WriteLine(" if (other is {0}) {{", ClassName);
378 writer.WriteLine(" return MergeFrom(({0}) other);", ClassName);
379 writer.WriteLine(" } else {");
380 writer.WriteLine(" base.MergeFrom(other);");
381 writer.WriteLine(" return this;");
382 writer.WriteLine(" }");
383 writer.WriteLine("}");
384 writer.WriteLine();
385 writer.WriteLine("public override Builder MergeFrom({0} other) {{", ClassName);
386 // Optimization: If other is the default instance, we know none of its
387 // fields are set so we can skip the merge.
388 writer.Indent();
389 writer.WriteLine("if (other == {0}.DefaultInstance) return this;", ClassName);
390 foreach (FieldDescriptor field in Descriptor.Fields) {
391 SourceGenerators.CreateFieldGenerator(field).GenerateMergingCode(writer);
392 }
Jon Skeet49fcd4f2009-01-27 14:43:10 +0000393 // if message type has extensions
394 if (Descriptor.Proto.ExtensionRangeCount > 0) {
395 writer.WriteLine(" this.MergeExtensionFields(other);");
396 }
Jon Skeet68036862008-10-22 13:30:34 +0100397 writer.WriteLine("this.MergeUnknownFields(other.UnknownFields);");
398 writer.WriteLine("return this;");
399 writer.Outdent();
400 writer.WriteLine("}");
401 writer.WriteLine();
402 }
403 }
404
405 private void GenerateBuilderParsingMethods(TextGenerator writer) {
406 List<FieldDescriptor> sortedFields = new List<FieldDescriptor>(Descriptor.Fields);
407 sortedFields.Sort((f1, f2) => f1.FieldNumber.CompareTo(f2.FieldNumber));
408
409 writer.WriteLine("public override Builder MergeFrom(pb::CodedInputStream input) {");
410 writer.WriteLine(" return MergeFrom(input, pb::ExtensionRegistry.Empty);");
411 writer.WriteLine("}");
412 writer.WriteLine();
413 writer.WriteLine("public override Builder MergeFrom(pb::CodedInputStream input, pb::ExtensionRegistry extensionRegistry) {");
414 writer.Indent();
Jon Skeet7de1aef2009-03-05 14:23:17 +0000415 writer.WriteLine("pb::UnknownFieldSet.Builder unknownFields = null;");
Jon Skeet68036862008-10-22 13:30:34 +0100416 writer.WriteLine("while (true) {");
417 writer.Indent();
418 writer.WriteLine("uint tag = input.ReadTag();");
419 writer.WriteLine("switch (tag) {");
420 writer.Indent();
421 writer.WriteLine("case 0: {"); // 0 signals EOF / limit reached
Jon Skeet7de1aef2009-03-05 14:23:17 +0000422 writer.WriteLine(" if (unknownFields != null) {");
423 writer.WriteLine(" this.UnknownFields = unknownFields.Build();");
424 writer.WriteLine(" }");
Jon Skeet68036862008-10-22 13:30:34 +0100425 writer.WriteLine(" return this;");
426 writer.WriteLine("}");
427 writer.WriteLine("default: {");
Jon Skeet7de1aef2009-03-05 14:23:17 +0000428 writer.WriteLine(" if (pb::WireFormat.IsEndGroupTag(tag)) {");
429 writer.WriteLine(" if (unknownFields != null) {");
430 writer.WriteLine(" this.UnknownFields = unknownFields.Build();");
431 writer.WriteLine(" }");
Jon Skeet68036862008-10-22 13:30:34 +0100432 writer.WriteLine(" return this;"); // it's an endgroup tag
433 writer.WriteLine(" }");
Jon Skeet7de1aef2009-03-05 14:23:17 +0000434 writer.WriteLine(" if (unknownFields == null) {"); // First unknown field - create builder now
435 writer.WriteLine(" unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields);");
436 writer.WriteLine(" }");
437 writer.WriteLine(" ParseUnknownField(input, unknownFields, extensionRegistry, tag);");
Jon Skeet68036862008-10-22 13:30:34 +0100438 writer.WriteLine(" break;");
439 writer.WriteLine("}");
440 foreach (FieldDescriptor field in sortedFields) {
Jon Skeet25a28582009-02-18 16:06:22 +0000441 uint tag = WireFormat.MakeTag(field);
Jon Skeet68036862008-10-22 13:30:34 +0100442 writer.WriteLine("case {0}: {{", tag);
443 writer.Indent();
444 SourceGenerators.CreateFieldGenerator(field).GenerateParsingCode(writer);
445 writer.WriteLine("break;");
446 writer.Outdent();
447 writer.WriteLine("}");
448 }
449 writer.Outdent();
450 writer.WriteLine("}");
451 writer.Outdent();
452 writer.WriteLine("}");
453 writer.Outdent();
454 writer.WriteLine("}");
455 writer.WriteLine();
456 }
457
458 private void GenerateIsInitialized(TextGenerator writer) {
459 writer.WriteLine("public override bool IsInitialized {");
460 writer.Indent();
461 writer.WriteLine("get {");
462 writer.Indent();
463
464 // Check that all required fields in this message are set.
465 // TODO(kenton): We can optimize this when we switch to putting all the
466 // "has" fields into a single bitfield.
467 foreach (FieldDescriptor field in Descriptor.Fields) {
468 if (field.IsRequired) {
Jon Skeet4cf9e3c2008-11-24 11:11:28 +0000469 writer.WriteLine("if (!has{0}) return false;", field.CSharpOptions.PropertyName);
Jon Skeet68036862008-10-22 13:30:34 +0100470 }
471 }
472
473 // Now check that all embedded messages are initialized.
474 foreach (FieldDescriptor field in Descriptor.Fields) {
475 if (field.FieldType != FieldType.Message ||
476 !HasRequiredFields(field.MessageType, new Dictionary<MessageDescriptor, object>())) {
477 continue;
478 }
Jon Skeetd6343be2008-11-12 23:39:44 +0000479 string propertyName = NameHelpers.UnderscoresToPascalCase(GetFieldName(field));
Jon Skeet68036862008-10-22 13:30:34 +0100480 if (field.IsRepeated) {
Jon Skeetd6343be2008-11-12 23:39:44 +0000481 writer.WriteLine("foreach ({0} element in {1}List) {{", GetClassName(field.MessageType), propertyName);
Jon Skeet68036862008-10-22 13:30:34 +0100482 writer.WriteLine(" if (!element.IsInitialized) return false;");
483 writer.WriteLine("}");
484 } else if (field.IsOptional) {
485 writer.WriteLine("if (Has{0}) {{", propertyName);
486 writer.WriteLine(" if (!{0}.IsInitialized) return false;", propertyName);
487 writer.WriteLine("}");
488 } else {
489 writer.WriteLine("if (!{0}.IsInitialized) return false;", propertyName);
490 }
491 }
492
493 if (Descriptor.Proto.ExtensionRangeCount > 0) {
494 writer.WriteLine("if (!ExtensionsAreInitialized) return false;");
495 }
496 writer.WriteLine("return true;");
497 writer.Outdent();
498 writer.WriteLine("}");
499 writer.Outdent();
500 writer.WriteLine("}");
501 writer.WriteLine();
502 }
Jon Skeetdf67f142009-06-05 19:29:36 +0100503
504 internal void GenerateExtensionRegistrationCode(TextGenerator writer) {
505 foreach (FieldDescriptor extension in Descriptor.Extensions) {
506 new ExtensionGenerator(extension).GenerateExtensionRegistrationCode(writer);
507 }
508 foreach (MessageDescriptor nestedMessage in Descriptor.NestedTypes) {
509 new MessageGenerator(nestedMessage).GenerateExtensionRegistrationCode(writer);
510 }
511 }
Jon Skeet68036862008-10-22 13:30:34 +0100512 }
513}