blob: 5385923107d6560ace7b60fe3f7d8417da495e0a [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);
143 // Note that the variable is needed just so we can access the property
144 writer.WriteLine(" pbd::FileDescriptor descriptor = {0}.Descriptor;", DescriptorUtil.GetFullUmbrellaClassName(Descriptor));
145 writer.WriteLine("}");
146
147 writer.Outdent();
148 writer.WriteLine("}");
149 writer.WriteLine();
Jon Skeet68036862008-10-22 13:30:34 +0100150 }
151
152 private void GenerateMessageSerializationMethods(TextGenerator writer) {
153 List<FieldDescriptor> sortedFields = new List<FieldDescriptor>(Descriptor.Fields);
154 sortedFields.Sort((f1, f2) => f1.FieldNumber.CompareTo(f2.FieldNumber));
155
156 List<ExtensionRange> sortedExtensions = new List<ExtensionRange>(Descriptor.Proto.ExtensionRangeList);
157 sortedExtensions.Sort((r1, r2) => (r1.Start.CompareTo(r2.Start)));
158
159 writer.WriteLine("public override void WriteTo(pb::CodedOutputStream output) {");
160 writer.Indent();
161 if (Descriptor.Proto.ExtensionRangeList.Count > 0) {
162 writer.WriteLine("pb::ExtendableMessage<{0}, {0}.Builder>.ExtensionWriter extensionWriter = CreateExtensionWriter(this);",
163 ClassName);
164 }
165
166 // Merge the fields and the extension ranges, both sorted by field number.
167 for (int i = 0, j = 0; i < Descriptor.Fields.Count || j < sortedExtensions.Count; ) {
168 if (i == Descriptor.Fields.Count) {
169 GenerateSerializeOneExtensionRange(writer, sortedExtensions[j++]);
170 } else if (j == sortedExtensions.Count) {
171 GenerateSerializeOneField(writer, sortedFields[i++]);
172 } else if (sortedFields[i].FieldNumber < sortedExtensions[j].Start) {
173 GenerateSerializeOneField(writer, sortedFields[i++]);
174 } else {
175 GenerateSerializeOneExtensionRange(writer, sortedExtensions[j++]);
176 }
177 }
178
179 if (Descriptor.Proto.Options.MessageSetWireFormat) {
180 writer.WriteLine("UnknownFields.WriteAsMessageSetTo(output);");
181 } else {
182 writer.WriteLine("UnknownFields.WriteTo(output);");
183 }
184
185 writer.Outdent();
186 writer.WriteLine("}");
187 writer.WriteLine();
188 writer.WriteLine("private int memoizedSerializedSize = -1;");
189 writer.WriteLine("public override int SerializedSize {");
190 writer.Indent();
191 writer.WriteLine("get {");
192 writer.Indent();
193 writer.WriteLine("int size = memoizedSerializedSize;");
194 writer.WriteLine("if (size != -1) return size;");
195 writer.WriteLine();
196 writer.WriteLine("size = 0;");
197 foreach (FieldDescriptor field in Descriptor.Fields) {
198 SourceGenerators.CreateFieldGenerator(field).GenerateSerializedSizeCode(writer);
199 }
200 if (Descriptor.Proto.ExtensionRangeCount > 0) {
201 writer.WriteLine("size += ExtensionsSerializedSize;");
202 }
203
204 if (Descriptor.Options.MessageSetWireFormat) {
205 writer.WriteLine("size += UnknownFields.SerializedSizeAsMessageSet;");
206 } else {
207 writer.WriteLine("size += UnknownFields.SerializedSize;");
208 }
209 writer.WriteLine("memoizedSerializedSize = size;");
210 writer.WriteLine("return size;");
211 writer.Outdent();
212 writer.WriteLine("}");
213 writer.Outdent();
214 writer.WriteLine("}");
215 writer.WriteLine();
216 }
217
Jon Skeet7ee85c42009-05-28 21:11:15 +0100218 private static void GenerateSerializeOneField(TextGenerator writer, FieldDescriptor fieldDescriptor) {
Jon Skeet68036862008-10-22 13:30:34 +0100219 SourceGenerators.CreateFieldGenerator(fieldDescriptor).GenerateSerializationCode(writer);
220 }
221
222 private static void GenerateSerializeOneExtensionRange(TextGenerator writer, ExtensionRange extensionRange) {
223 writer.WriteLine("extensionWriter.WriteUntil({0}, output);", extensionRange.End);
224 }
225
226 private void GenerateParseFromMethods(TextGenerator writer) {
227 // Note: These are separate from GenerateMessageSerializationMethods()
228 // because they need to be generated even for messages that are optimized
229 // for code size.
230
231 writer.WriteLine("public static {0} ParseFrom(pb::ByteString data) {{", ClassName);
232 writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed();");
233 writer.WriteLine("}");
234 writer.WriteLine("public static {0} ParseFrom(pb::ByteString data, pb::ExtensionRegistry extensionRegistry) {{", ClassName);
235 writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();");
236 writer.WriteLine("}");
237 writer.WriteLine("public static {0} ParseFrom(byte[] data) {{", ClassName);
238 writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed();");
239 writer.WriteLine("}");
240 writer.WriteLine("public static {0} ParseFrom(byte[] data, pb::ExtensionRegistry extensionRegistry) {{", ClassName);
241 writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();");
242 writer.WriteLine("}");
243 writer.WriteLine("public static {0} ParseFrom(global::System.IO.Stream input) {{", ClassName);
244 writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed();");
245 writer.WriteLine("}");
246 writer.WriteLine("public static {0} ParseFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) {{", ClassName);
247 writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();");
248 writer.WriteLine("}");
Jon Skeet2e6dc122009-05-29 06:34:52 +0100249 writer.WriteLine("public static {0} ParseDelimitedFrom(global::System.IO.Stream input) {{", ClassName);
250 writer.WriteLine(" return CreateBuilder().MergeDelimitedFrom(input).BuildParsed();");
251 writer.WriteLine("}");
252 writer.WriteLine("public static {0} ParseDelimitedFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) {{", ClassName);
253 writer.WriteLine(" return CreateBuilder().MergeDelimitedFrom(input, extensionRegistry).BuildParsed();");
254 writer.WriteLine("}");
Jon Skeet68036862008-10-22 13:30:34 +0100255 writer.WriteLine("public static {0} ParseFrom(pb::CodedInputStream input) {{", ClassName);
256 writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed();");
257 writer.WriteLine("}");
258 writer.WriteLine("public static {0} ParseFrom(pb::CodedInputStream input, pb::ExtensionRegistry extensionRegistry) {{", ClassName);
259 writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();");
260 writer.WriteLine("}");
261 }
262
263 /// <summary>
264 /// Returns whether or not the specified message type has any required fields.
265 /// If it doesn't, calls to check for initialization can be optimised.
266 /// TODO(jonskeet): Move this into MessageDescriptor?
267 /// </summary>
268 private static bool HasRequiredFields(MessageDescriptor descriptor, Dictionary<MessageDescriptor,object> alreadySeen) {
269 if (alreadySeen.ContainsKey(descriptor)) {
270 // The type is already in cache. This means that either:
271 // a. The type has no required fields.
272 // b. We are in the midst of checking if the type has required fields,
273 // somewhere up the stack. In this case, we know that if the type
274 // has any required fields, they'll be found when we return to it,
275 // and the whole call to HasRequiredFields() will return true.
276 // Therefore, we don't have to check if this type has required fields
277 // here.
278 return false;
279 }
280 alreadySeen[descriptor] = descriptor; // Value is irrelevant
281
282 // If the type has extensions, an extension with message type could contain
283 // required fields, so we have to be conservative and assume such an
284 // extension exists.
285 if (descriptor.Extensions.Count > 0) {
286 return true;
287 }
288
289 foreach (FieldDescriptor field in descriptor.Fields) {
290 if (field.IsRequired) {
291 return true;
292 }
293 // Message or group
294 if (field.MappedType == MappedType.Message) {
295 if (HasRequiredFields(field.MessageType, alreadySeen)) {
296 return true;
297 }
298 }
299 }
300 return false;
301 }
302
303 private void GenerateBuilder(TextGenerator writer) {
304 writer.WriteLine("public static Builder CreateBuilder() { return new Builder(); }");
Jon Skeete81a9d72009-02-24 16:50:56 +0000305 writer.WriteLine("public override Builder ToBuilder() { return CreateBuilder(this); }");
Jon Skeet68036862008-10-22 13:30:34 +0100306 writer.WriteLine("public override Builder CreateBuilderForType() { return new Builder(); }");
307 writer.WriteLine("public static Builder CreateBuilder({0} prototype) {{", ClassName);
308 writer.WriteLine(" return (Builder) new Builder().MergeFrom(prototype);");
309 writer.WriteLine("}");
310 writer.WriteLine();
311 writer.WriteLine("{0} sealed partial class Builder : pb::{2}Builder<{1}, Builder> {{",
312 ClassAccessLevel, ClassName, Descriptor.Proto.ExtensionRangeCount > 0 ? "Extendable" : "Generated");
313 writer.Indent();
314 writer.WriteLine("protected override Builder ThisBuilder {");
315 writer.WriteLine(" get { return this; }");
316 writer.WriteLine("}");
317 GenerateCommonBuilderMethods(writer);
318 if (Descriptor.File.Options.OptimizeFor == FileOptions.Types.OptimizeMode.SPEED) {
319 GenerateBuilderParsingMethods(writer);
320 }
321 foreach (FieldDescriptor field in Descriptor.Fields) {
322 writer.WriteLine();
323 // No field comment :(
324 SourceGenerators.CreateFieldGenerator(field).GenerateBuilderMembers(writer);
325 }
326 writer.Outdent();
327 writer.WriteLine("}");
Jon Skeet68036862008-10-22 13:30:34 +0100328 }
329
330 private void GenerateCommonBuilderMethods(TextGenerator writer) {
331 writer.WriteLine("{0} Builder() {{}}", ClassAccessLevel);
332 writer.WriteLine();
333 writer.WriteLine("{0} result = new {0}();", ClassName);
334 writer.WriteLine();
335 writer.WriteLine("protected override {0} MessageBeingBuilt {{", ClassName);
336 writer.WriteLine(" get { return result; }");
337 writer.WriteLine("}");
338 writer.WriteLine();
339 writer.WriteLine("public override Builder Clear() {");
340 writer.WriteLine(" result = new {0}();", ClassName);
341 writer.WriteLine(" return this;");
342 writer.WriteLine("}");
343 writer.WriteLine();
344 writer.WriteLine("public override Builder Clone() {");
345 writer.WriteLine(" return new Builder().MergeFrom(result);");
346 writer.WriteLine("}");
347 writer.WriteLine();
348 writer.WriteLine("public override pbd::MessageDescriptor DescriptorForType {");
349 writer.WriteLine(" get {{ return {0}.Descriptor; }}", ClassName);
350 writer.WriteLine("}");
351 writer.WriteLine();
352 writer.WriteLine("public override {0} DefaultInstanceForType {{", ClassName);
353 writer.WriteLine(" get {{ return {0}.DefaultInstance; }}", ClassName);
354 writer.WriteLine("}");
355 writer.WriteLine();
356
357 writer.WriteLine("public override {0} BuildPartial() {{", ClassName);
358 writer.Indent();
Jon Skeet2e6dc122009-05-29 06:34:52 +0100359 writer.WriteLine("if (result == null) {");
360 writer.WriteLine(" throw new global::System.InvalidOperationException(\"build() has already been called on this Builder\");");
361 writer.WriteLine("}");
Jon Skeet68036862008-10-22 13:30:34 +0100362 foreach (FieldDescriptor field in Descriptor.Fields) {
363 SourceGenerators.CreateFieldGenerator(field).GenerateBuildingCode(writer);
364 }
365 writer.WriteLine("{0} returnMe = result;", ClassName);
366 writer.WriteLine("result = null;");
367 writer.WriteLine("return returnMe;");
368 writer.Outdent();
369 writer.WriteLine("}");
370 writer.WriteLine();
371
372 if (Descriptor.File.Options.OptimizeFor == FileOptions.Types.OptimizeMode.SPEED) {
373 writer.WriteLine("public override Builder MergeFrom(pb::IMessage other) {");
374 writer.WriteLine(" if (other is {0}) {{", ClassName);
375 writer.WriteLine(" return MergeFrom(({0}) other);", ClassName);
376 writer.WriteLine(" } else {");
377 writer.WriteLine(" base.MergeFrom(other);");
378 writer.WriteLine(" return this;");
379 writer.WriteLine(" }");
380 writer.WriteLine("}");
381 writer.WriteLine();
382 writer.WriteLine("public override Builder MergeFrom({0} other) {{", ClassName);
383 // Optimization: If other is the default instance, we know none of its
384 // fields are set so we can skip the merge.
385 writer.Indent();
386 writer.WriteLine("if (other == {0}.DefaultInstance) return this;", ClassName);
387 foreach (FieldDescriptor field in Descriptor.Fields) {
388 SourceGenerators.CreateFieldGenerator(field).GenerateMergingCode(writer);
389 }
Jon Skeet49fcd4f2009-01-27 14:43:10 +0000390 // if message type has extensions
391 if (Descriptor.Proto.ExtensionRangeCount > 0) {
392 writer.WriteLine(" this.MergeExtensionFields(other);");
393 }
Jon Skeet68036862008-10-22 13:30:34 +0100394 writer.WriteLine("this.MergeUnknownFields(other.UnknownFields);");
395 writer.WriteLine("return this;");
396 writer.Outdent();
397 writer.WriteLine("}");
398 writer.WriteLine();
399 }
400 }
401
402 private void GenerateBuilderParsingMethods(TextGenerator writer) {
403 List<FieldDescriptor> sortedFields = new List<FieldDescriptor>(Descriptor.Fields);
404 sortedFields.Sort((f1, f2) => f1.FieldNumber.CompareTo(f2.FieldNumber));
405
406 writer.WriteLine("public override Builder MergeFrom(pb::CodedInputStream input) {");
407 writer.WriteLine(" return MergeFrom(input, pb::ExtensionRegistry.Empty);");
408 writer.WriteLine("}");
409 writer.WriteLine();
410 writer.WriteLine("public override Builder MergeFrom(pb::CodedInputStream input, pb::ExtensionRegistry extensionRegistry) {");
411 writer.Indent();
Jon Skeet7de1aef2009-03-05 14:23:17 +0000412 writer.WriteLine("pb::UnknownFieldSet.Builder unknownFields = null;");
Jon Skeet68036862008-10-22 13:30:34 +0100413 writer.WriteLine("while (true) {");
414 writer.Indent();
415 writer.WriteLine("uint tag = input.ReadTag();");
416 writer.WriteLine("switch (tag) {");
417 writer.Indent();
418 writer.WriteLine("case 0: {"); // 0 signals EOF / limit reached
Jon Skeet7de1aef2009-03-05 14:23:17 +0000419 writer.WriteLine(" if (unknownFields != null) {");
420 writer.WriteLine(" this.UnknownFields = unknownFields.Build();");
421 writer.WriteLine(" }");
Jon Skeet68036862008-10-22 13:30:34 +0100422 writer.WriteLine(" return this;");
423 writer.WriteLine("}");
424 writer.WriteLine("default: {");
Jon Skeet7de1aef2009-03-05 14:23:17 +0000425 writer.WriteLine(" if (pb::WireFormat.IsEndGroupTag(tag)) {");
426 writer.WriteLine(" if (unknownFields != null) {");
427 writer.WriteLine(" this.UnknownFields = unknownFields.Build();");
428 writer.WriteLine(" }");
Jon Skeet68036862008-10-22 13:30:34 +0100429 writer.WriteLine(" return this;"); // it's an endgroup tag
430 writer.WriteLine(" }");
Jon Skeet7de1aef2009-03-05 14:23:17 +0000431 writer.WriteLine(" if (unknownFields == null) {"); // First unknown field - create builder now
432 writer.WriteLine(" unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields);");
433 writer.WriteLine(" }");
434 writer.WriteLine(" ParseUnknownField(input, unknownFields, extensionRegistry, tag);");
Jon Skeet68036862008-10-22 13:30:34 +0100435 writer.WriteLine(" break;");
436 writer.WriteLine("}");
437 foreach (FieldDescriptor field in sortedFields) {
Jon Skeet25a28582009-02-18 16:06:22 +0000438 uint tag = WireFormat.MakeTag(field);
Jon Skeet68036862008-10-22 13:30:34 +0100439 writer.WriteLine("case {0}: {{", tag);
440 writer.Indent();
441 SourceGenerators.CreateFieldGenerator(field).GenerateParsingCode(writer);
442 writer.WriteLine("break;");
443 writer.Outdent();
444 writer.WriteLine("}");
445 }
446 writer.Outdent();
447 writer.WriteLine("}");
448 writer.Outdent();
449 writer.WriteLine("}");
450 writer.Outdent();
451 writer.WriteLine("}");
452 writer.WriteLine();
453 }
454
455 private void GenerateIsInitialized(TextGenerator writer) {
456 writer.WriteLine("public override bool IsInitialized {");
457 writer.Indent();
458 writer.WriteLine("get {");
459 writer.Indent();
460
461 // Check that all required fields in this message are set.
462 // TODO(kenton): We can optimize this when we switch to putting all the
463 // "has" fields into a single bitfield.
464 foreach (FieldDescriptor field in Descriptor.Fields) {
465 if (field.IsRequired) {
Jon Skeet4cf9e3c2008-11-24 11:11:28 +0000466 writer.WriteLine("if (!has{0}) return false;", field.CSharpOptions.PropertyName);
Jon Skeet68036862008-10-22 13:30:34 +0100467 }
468 }
469
470 // Now check that all embedded messages are initialized.
471 foreach (FieldDescriptor field in Descriptor.Fields) {
472 if (field.FieldType != FieldType.Message ||
473 !HasRequiredFields(field.MessageType, new Dictionary<MessageDescriptor, object>())) {
474 continue;
475 }
Jon Skeetd6343be2008-11-12 23:39:44 +0000476 string propertyName = NameHelpers.UnderscoresToPascalCase(GetFieldName(field));
Jon Skeet68036862008-10-22 13:30:34 +0100477 if (field.IsRepeated) {
Jon Skeetd6343be2008-11-12 23:39:44 +0000478 writer.WriteLine("foreach ({0} element in {1}List) {{", GetClassName(field.MessageType), propertyName);
Jon Skeet68036862008-10-22 13:30:34 +0100479 writer.WriteLine(" if (!element.IsInitialized) return false;");
480 writer.WriteLine("}");
481 } else if (field.IsOptional) {
482 writer.WriteLine("if (Has{0}) {{", propertyName);
483 writer.WriteLine(" if (!{0}.IsInitialized) return false;", propertyName);
484 writer.WriteLine("}");
485 } else {
486 writer.WriteLine("if (!{0}.IsInitialized) return false;", propertyName);
487 }
488 }
489
490 if (Descriptor.Proto.ExtensionRangeCount > 0) {
491 writer.WriteLine("if (!ExtensionsAreInitialized) return false;");
492 }
493 writer.WriteLine("return true;");
494 writer.Outdent();
495 writer.WriteLine("}");
496 writer.Outdent();
497 writer.WriteLine("}");
498 writer.WriteLine();
499 }
Jon Skeetdf67f142009-06-05 19:29:36 +0100500
501 internal void GenerateExtensionRegistrationCode(TextGenerator writer) {
502 foreach (FieldDescriptor extension in Descriptor.Extensions) {
503 new ExtensionGenerator(extension).GenerateExtensionRegistrationCode(writer);
504 }
505 foreach (MessageDescriptor nestedMessage in Descriptor.NestedTypes) {
506 new MessageGenerator(nestedMessage).GenerateExtensionRegistrationCode(writer);
507 }
508 }
Jon Skeet68036862008-10-22 13:30:34 +0100509 }
510}