blob: 4b9830036b7e34c6b9ffd06ec4d96f0823e28e8d [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 Skeet68036862008-10-22 13:30:34 +010042 writer.WriteLine("{0} static readonly pbd::MessageDescriptor internal__{1}__Descriptor", access, identifier);
43 if (Descriptor.ContainingType == null) {
44 writer.WriteLine(" = Descriptor.MessageTypes[{0}];", Descriptor.Index);
45 } else {
46 writer.WriteLine(" = internal__{0}__Descriptor.NestedTypes[{1}];", GetUniqueFileScopeIdentifier(Descriptor.ContainingType), Descriptor.Index);
47 }
48 writer.WriteLine("{0} static pb::FieldAccess.FieldAccessorTable<{1}, {1}.Builder> internal__{2}__FieldAccessorTable",
49 access, FullClassName, identifier);
50 writer.WriteLine(" = new pb::FieldAccess.FieldAccessorTable<{0}, {0}.Builder>(internal__{1}__Descriptor,",
51 FullClassName, identifier);
52 writer.Print(" new string[] { ");
53 foreach (FieldDescriptor field in Descriptor.Fields) {
Jon Skeet4cf9e3c2008-11-24 11:11:28 +000054 writer.Write("\"{0}\", ", field.CSharpOptions.PropertyName);
Jon Skeet68036862008-10-22 13:30:34 +010055 }
56 writer.WriteLine("});");
57
58 // Generate static members for all nested types.
59 foreach (MessageDescriptor nestedMessage in Descriptor.NestedTypes) {
60 new MessageGenerator(nestedMessage).GenerateStaticVariables(writer);
61 }
62 }
63
64 public void Generate(TextGenerator writer) {
65 writer.WriteLine("{0} sealed partial class {1} : pb::{2}Message<{1}, {1}.Builder> {{",
66 ClassAccessLevel, ClassName, Descriptor.Proto.ExtensionRangeCount > 0 ? "Extendable" : "Generated");
67 writer.Indent();
68 // Must call BuildPartial() to make sure all lists are made read-only
69 writer.WriteLine("private static readonly {0} defaultInstance = new Builder().BuildPartial();", ClassName);
70 writer.WriteLine("public static {0} DefaultInstance {{", ClassName);
71 writer.WriteLine(" get { return defaultInstance; }");
72 writer.WriteLine("}");
73 writer.WriteLine();
74 writer.WriteLine("public override {0} DefaultInstanceForType {{", ClassName);
75 writer.WriteLine(" get { return defaultInstance; }");
76 writer.WriteLine("}");
77 writer.WriteLine();
78 writer.WriteLine("protected override {0} ThisMessage {{", ClassName);
79 writer.WriteLine(" get { return this; }");
80 writer.WriteLine("}");
81 writer.WriteLine();
82 writer.WriteLine("public static pbd::MessageDescriptor Descriptor {");
Jon Skeetd6343be2008-11-12 23:39:44 +000083 writer.WriteLine(" get {{ return {0}.internal__{1}__Descriptor; }}", DescriptorUtil.GetFullUmbrellaClassName(Descriptor),
Jon Skeet68036862008-10-22 13:30:34 +010084 GetUniqueFileScopeIdentifier(Descriptor));
85 writer.WriteLine("}");
86 writer.WriteLine();
87 writer.WriteLine("protected override pb::FieldAccess.FieldAccessorTable<{0}, {0}.Builder> InternalFieldAccessors {{", ClassName);
Jon Skeetd6343be2008-11-12 23:39:44 +000088 writer.WriteLine(" get {{ return {0}.internal__{1}__FieldAccessorTable; }}", DescriptorUtil.GetFullUmbrellaClassName(Descriptor),
Jon Skeet68036862008-10-22 13:30:34 +010089 GetUniqueFileScopeIdentifier(Descriptor));
90 writer.WriteLine("}");
91 writer.WriteLine();
92
93 // Extensions don't need to go in an extra nested type
94 WriteChildren(writer, null, Descriptor.Extensions);
95
96 if (Descriptor.EnumTypes.Count + Descriptor.NestedTypes.Count > 0) {
97 writer.WriteLine("#region Nested types");
98 writer.WriteLine("public static class Types {");
99 writer.Indent();
100 WriteChildren(writer, null, Descriptor.EnumTypes);
101 WriteChildren(writer, null, Descriptor.NestedTypes);
102 writer.Outdent();
103 writer.WriteLine("}");
104 writer.WriteLine("#endregion");
105 writer.WriteLine();
106 }
107
108 foreach(FieldDescriptor fieldDescriptor in Descriptor.Fields) {
109 // Rats: we lose the debug comment here :(
Jon Skeet7ee85c42009-05-28 21:11:15 +0100110 writer.WriteLine("public const int {0} = {1};", GetFieldConstantName(fieldDescriptor), fieldDescriptor.FieldNumber);
Jon Skeet68036862008-10-22 13:30:34 +0100111 SourceGenerators.CreateFieldGenerator(fieldDescriptor).GenerateMembers(writer);
112 writer.WriteLine();
113 }
114
115 if (Descriptor.File.Options.OptimizeFor == FileOptions.Types.OptimizeMode.SPEED) {
116 GenerateIsInitialized(writer);
117 GenerateMessageSerializationMethods(writer);
118 }
119
120 GenerateParseFromMethods(writer);
121 GenerateBuilder(writer);
122 }
123
124 private void GenerateMessageSerializationMethods(TextGenerator writer) {
125 List<FieldDescriptor> sortedFields = new List<FieldDescriptor>(Descriptor.Fields);
126 sortedFields.Sort((f1, f2) => f1.FieldNumber.CompareTo(f2.FieldNumber));
127
128 List<ExtensionRange> sortedExtensions = new List<ExtensionRange>(Descriptor.Proto.ExtensionRangeList);
129 sortedExtensions.Sort((r1, r2) => (r1.Start.CompareTo(r2.Start)));
130
131 writer.WriteLine("public override void WriteTo(pb::CodedOutputStream output) {");
132 writer.Indent();
133 if (Descriptor.Proto.ExtensionRangeList.Count > 0) {
134 writer.WriteLine("pb::ExtendableMessage<{0}, {0}.Builder>.ExtensionWriter extensionWriter = CreateExtensionWriter(this);",
135 ClassName);
136 }
137
138 // Merge the fields and the extension ranges, both sorted by field number.
139 for (int i = 0, j = 0; i < Descriptor.Fields.Count || j < sortedExtensions.Count; ) {
140 if (i == Descriptor.Fields.Count) {
141 GenerateSerializeOneExtensionRange(writer, sortedExtensions[j++]);
142 } else if (j == sortedExtensions.Count) {
143 GenerateSerializeOneField(writer, sortedFields[i++]);
144 } else if (sortedFields[i].FieldNumber < sortedExtensions[j].Start) {
145 GenerateSerializeOneField(writer, sortedFields[i++]);
146 } else {
147 GenerateSerializeOneExtensionRange(writer, sortedExtensions[j++]);
148 }
149 }
150
151 if (Descriptor.Proto.Options.MessageSetWireFormat) {
152 writer.WriteLine("UnknownFields.WriteAsMessageSetTo(output);");
153 } else {
154 writer.WriteLine("UnknownFields.WriteTo(output);");
155 }
156
157 writer.Outdent();
158 writer.WriteLine("}");
159 writer.WriteLine();
160 writer.WriteLine("private int memoizedSerializedSize = -1;");
161 writer.WriteLine("public override int SerializedSize {");
162 writer.Indent();
163 writer.WriteLine("get {");
164 writer.Indent();
165 writer.WriteLine("int size = memoizedSerializedSize;");
166 writer.WriteLine("if (size != -1) return size;");
167 writer.WriteLine();
168 writer.WriteLine("size = 0;");
169 foreach (FieldDescriptor field in Descriptor.Fields) {
170 SourceGenerators.CreateFieldGenerator(field).GenerateSerializedSizeCode(writer);
171 }
172 if (Descriptor.Proto.ExtensionRangeCount > 0) {
173 writer.WriteLine("size += ExtensionsSerializedSize;");
174 }
175
176 if (Descriptor.Options.MessageSetWireFormat) {
177 writer.WriteLine("size += UnknownFields.SerializedSizeAsMessageSet;");
178 } else {
179 writer.WriteLine("size += UnknownFields.SerializedSize;");
180 }
181 writer.WriteLine("memoizedSerializedSize = size;");
182 writer.WriteLine("return size;");
183 writer.Outdent();
184 writer.WriteLine("}");
185 writer.Outdent();
186 writer.WriteLine("}");
187 writer.WriteLine();
188 }
189
Jon Skeet7ee85c42009-05-28 21:11:15 +0100190 private static void GenerateSerializeOneField(TextGenerator writer, FieldDescriptor fieldDescriptor) {
Jon Skeet68036862008-10-22 13:30:34 +0100191 SourceGenerators.CreateFieldGenerator(fieldDescriptor).GenerateSerializationCode(writer);
192 }
193
194 private static void GenerateSerializeOneExtensionRange(TextGenerator writer, ExtensionRange extensionRange) {
195 writer.WriteLine("extensionWriter.WriteUntil({0}, output);", extensionRange.End);
196 }
197
198 private void GenerateParseFromMethods(TextGenerator writer) {
199 // Note: These are separate from GenerateMessageSerializationMethods()
200 // because they need to be generated even for messages that are optimized
201 // for code size.
202
203 writer.WriteLine("public static {0} ParseFrom(pb::ByteString data) {{", ClassName);
204 writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed();");
205 writer.WriteLine("}");
206 writer.WriteLine("public static {0} ParseFrom(pb::ByteString data, pb::ExtensionRegistry extensionRegistry) {{", ClassName);
207 writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();");
208 writer.WriteLine("}");
209 writer.WriteLine("public static {0} ParseFrom(byte[] data) {{", ClassName);
210 writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed();");
211 writer.WriteLine("}");
212 writer.WriteLine("public static {0} ParseFrom(byte[] data, pb::ExtensionRegistry extensionRegistry) {{", ClassName);
213 writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();");
214 writer.WriteLine("}");
215 writer.WriteLine("public static {0} ParseFrom(global::System.IO.Stream input) {{", ClassName);
216 writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed();");
217 writer.WriteLine("}");
218 writer.WriteLine("public static {0} ParseFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) {{", ClassName);
219 writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();");
220 writer.WriteLine("}");
Jon Skeet2e6dc122009-05-29 06:34:52 +0100221 writer.WriteLine("public static {0} ParseDelimitedFrom(global::System.IO.Stream input) {{", ClassName);
222 writer.WriteLine(" return CreateBuilder().MergeDelimitedFrom(input).BuildParsed();");
223 writer.WriteLine("}");
224 writer.WriteLine("public static {0} ParseDelimitedFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) {{", ClassName);
225 writer.WriteLine(" return CreateBuilder().MergeDelimitedFrom(input, extensionRegistry).BuildParsed();");
226 writer.WriteLine("}");
Jon Skeet68036862008-10-22 13:30:34 +0100227 writer.WriteLine("public static {0} ParseFrom(pb::CodedInputStream input) {{", ClassName);
228 writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed();");
229 writer.WriteLine("}");
230 writer.WriteLine("public static {0} ParseFrom(pb::CodedInputStream input, pb::ExtensionRegistry extensionRegistry) {{", ClassName);
231 writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();");
232 writer.WriteLine("}");
233 }
234
235 /// <summary>
236 /// Returns whether or not the specified message type has any required fields.
237 /// If it doesn't, calls to check for initialization can be optimised.
238 /// TODO(jonskeet): Move this into MessageDescriptor?
239 /// </summary>
240 private static bool HasRequiredFields(MessageDescriptor descriptor, Dictionary<MessageDescriptor,object> alreadySeen) {
241 if (alreadySeen.ContainsKey(descriptor)) {
242 // The type is already in cache. This means that either:
243 // a. The type has no required fields.
244 // b. We are in the midst of checking if the type has required fields,
245 // somewhere up the stack. In this case, we know that if the type
246 // has any required fields, they'll be found when we return to it,
247 // and the whole call to HasRequiredFields() will return true.
248 // Therefore, we don't have to check if this type has required fields
249 // here.
250 return false;
251 }
252 alreadySeen[descriptor] = descriptor; // Value is irrelevant
253
254 // If the type has extensions, an extension with message type could contain
255 // required fields, so we have to be conservative and assume such an
256 // extension exists.
257 if (descriptor.Extensions.Count > 0) {
258 return true;
259 }
260
261 foreach (FieldDescriptor field in descriptor.Fields) {
262 if (field.IsRequired) {
263 return true;
264 }
265 // Message or group
266 if (field.MappedType == MappedType.Message) {
267 if (HasRequiredFields(field.MessageType, alreadySeen)) {
268 return true;
269 }
270 }
271 }
272 return false;
273 }
274
275 private void GenerateBuilder(TextGenerator writer) {
276 writer.WriteLine("public static Builder CreateBuilder() { return new Builder(); }");
Jon Skeete81a9d72009-02-24 16:50:56 +0000277 writer.WriteLine("public override Builder ToBuilder() { return CreateBuilder(this); }");
Jon Skeet68036862008-10-22 13:30:34 +0100278 writer.WriteLine("public override Builder CreateBuilderForType() { return new Builder(); }");
279 writer.WriteLine("public static Builder CreateBuilder({0} prototype) {{", ClassName);
280 writer.WriteLine(" return (Builder) new Builder().MergeFrom(prototype);");
281 writer.WriteLine("}");
282 writer.WriteLine();
283 writer.WriteLine("{0} sealed partial class Builder : pb::{2}Builder<{1}, Builder> {{",
284 ClassAccessLevel, ClassName, Descriptor.Proto.ExtensionRangeCount > 0 ? "Extendable" : "Generated");
285 writer.Indent();
286 writer.WriteLine("protected override Builder ThisBuilder {");
287 writer.WriteLine(" get { return this; }");
288 writer.WriteLine("}");
289 GenerateCommonBuilderMethods(writer);
290 if (Descriptor.File.Options.OptimizeFor == FileOptions.Types.OptimizeMode.SPEED) {
291 GenerateBuilderParsingMethods(writer);
292 }
293 foreach (FieldDescriptor field in Descriptor.Fields) {
294 writer.WriteLine();
295 // No field comment :(
296 SourceGenerators.CreateFieldGenerator(field).GenerateBuilderMembers(writer);
297 }
298 writer.Outdent();
299 writer.WriteLine("}");
300 writer.Outdent();
301 writer.WriteLine("}");
302 writer.WriteLine();
303 }
304
305 private void GenerateCommonBuilderMethods(TextGenerator writer) {
306 writer.WriteLine("{0} Builder() {{}}", ClassAccessLevel);
307 writer.WriteLine();
308 writer.WriteLine("{0} result = new {0}();", ClassName);
309 writer.WriteLine();
310 writer.WriteLine("protected override {0} MessageBeingBuilt {{", ClassName);
311 writer.WriteLine(" get { return result; }");
312 writer.WriteLine("}");
313 writer.WriteLine();
314 writer.WriteLine("public override Builder Clear() {");
315 writer.WriteLine(" result = new {0}();", ClassName);
316 writer.WriteLine(" return this;");
317 writer.WriteLine("}");
318 writer.WriteLine();
319 writer.WriteLine("public override Builder Clone() {");
320 writer.WriteLine(" return new Builder().MergeFrom(result);");
321 writer.WriteLine("}");
322 writer.WriteLine();
323 writer.WriteLine("public override pbd::MessageDescriptor DescriptorForType {");
324 writer.WriteLine(" get {{ return {0}.Descriptor; }}", ClassName);
325 writer.WriteLine("}");
326 writer.WriteLine();
327 writer.WriteLine("public override {0} DefaultInstanceForType {{", ClassName);
328 writer.WriteLine(" get {{ return {0}.DefaultInstance; }}", ClassName);
329 writer.WriteLine("}");
330 writer.WriteLine();
331
332 writer.WriteLine("public override {0} BuildPartial() {{", ClassName);
333 writer.Indent();
Jon Skeet2e6dc122009-05-29 06:34:52 +0100334 writer.WriteLine("if (result == null) {");
335 writer.WriteLine(" throw new global::System.InvalidOperationException(\"build() has already been called on this Builder\");");
336 writer.WriteLine("}");
Jon Skeet68036862008-10-22 13:30:34 +0100337 foreach (FieldDescriptor field in Descriptor.Fields) {
338 SourceGenerators.CreateFieldGenerator(field).GenerateBuildingCode(writer);
339 }
340 writer.WriteLine("{0} returnMe = result;", ClassName);
341 writer.WriteLine("result = null;");
342 writer.WriteLine("return returnMe;");
343 writer.Outdent();
344 writer.WriteLine("}");
345 writer.WriteLine();
346
347 if (Descriptor.File.Options.OptimizeFor == FileOptions.Types.OptimizeMode.SPEED) {
348 writer.WriteLine("public override Builder MergeFrom(pb::IMessage other) {");
349 writer.WriteLine(" if (other is {0}) {{", ClassName);
350 writer.WriteLine(" return MergeFrom(({0}) other);", ClassName);
351 writer.WriteLine(" } else {");
352 writer.WriteLine(" base.MergeFrom(other);");
353 writer.WriteLine(" return this;");
354 writer.WriteLine(" }");
355 writer.WriteLine("}");
356 writer.WriteLine();
357 writer.WriteLine("public override Builder MergeFrom({0} other) {{", ClassName);
358 // Optimization: If other is the default instance, we know none of its
359 // fields are set so we can skip the merge.
360 writer.Indent();
361 writer.WriteLine("if (other == {0}.DefaultInstance) return this;", ClassName);
362 foreach (FieldDescriptor field in Descriptor.Fields) {
363 SourceGenerators.CreateFieldGenerator(field).GenerateMergingCode(writer);
364 }
Jon Skeet49fcd4f2009-01-27 14:43:10 +0000365 // if message type has extensions
366 if (Descriptor.Proto.ExtensionRangeCount > 0) {
367 writer.WriteLine(" this.MergeExtensionFields(other);");
368 }
Jon Skeet68036862008-10-22 13:30:34 +0100369 writer.WriteLine("this.MergeUnknownFields(other.UnknownFields);");
370 writer.WriteLine("return this;");
371 writer.Outdent();
372 writer.WriteLine("}");
373 writer.WriteLine();
374 }
375 }
376
377 private void GenerateBuilderParsingMethods(TextGenerator writer) {
378 List<FieldDescriptor> sortedFields = new List<FieldDescriptor>(Descriptor.Fields);
379 sortedFields.Sort((f1, f2) => f1.FieldNumber.CompareTo(f2.FieldNumber));
380
381 writer.WriteLine("public override Builder MergeFrom(pb::CodedInputStream input) {");
382 writer.WriteLine(" return MergeFrom(input, pb::ExtensionRegistry.Empty);");
383 writer.WriteLine("}");
384 writer.WriteLine();
385 writer.WriteLine("public override Builder MergeFrom(pb::CodedInputStream input, pb::ExtensionRegistry extensionRegistry) {");
386 writer.Indent();
Jon Skeet7de1aef2009-03-05 14:23:17 +0000387 writer.WriteLine("pb::UnknownFieldSet.Builder unknownFields = null;");
Jon Skeet68036862008-10-22 13:30:34 +0100388 writer.WriteLine("while (true) {");
389 writer.Indent();
390 writer.WriteLine("uint tag = input.ReadTag();");
391 writer.WriteLine("switch (tag) {");
392 writer.Indent();
393 writer.WriteLine("case 0: {"); // 0 signals EOF / limit reached
Jon Skeet7de1aef2009-03-05 14:23:17 +0000394 writer.WriteLine(" if (unknownFields != null) {");
395 writer.WriteLine(" this.UnknownFields = unknownFields.Build();");
396 writer.WriteLine(" }");
Jon Skeet68036862008-10-22 13:30:34 +0100397 writer.WriteLine(" return this;");
398 writer.WriteLine("}");
399 writer.WriteLine("default: {");
Jon Skeet7de1aef2009-03-05 14:23:17 +0000400 writer.WriteLine(" if (pb::WireFormat.IsEndGroupTag(tag)) {");
401 writer.WriteLine(" if (unknownFields != null) {");
402 writer.WriteLine(" this.UnknownFields = unknownFields.Build();");
403 writer.WriteLine(" }");
Jon Skeet68036862008-10-22 13:30:34 +0100404 writer.WriteLine(" return this;"); // it's an endgroup tag
405 writer.WriteLine(" }");
Jon Skeet7de1aef2009-03-05 14:23:17 +0000406 writer.WriteLine(" if (unknownFields == null) {"); // First unknown field - create builder now
407 writer.WriteLine(" unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields);");
408 writer.WriteLine(" }");
409 writer.WriteLine(" ParseUnknownField(input, unknownFields, extensionRegistry, tag);");
Jon Skeet68036862008-10-22 13:30:34 +0100410 writer.WriteLine(" break;");
411 writer.WriteLine("}");
412 foreach (FieldDescriptor field in sortedFields) {
Jon Skeet25a28582009-02-18 16:06:22 +0000413 uint tag = WireFormat.MakeTag(field);
Jon Skeet68036862008-10-22 13:30:34 +0100414 writer.WriteLine("case {0}: {{", tag);
415 writer.Indent();
416 SourceGenerators.CreateFieldGenerator(field).GenerateParsingCode(writer);
417 writer.WriteLine("break;");
418 writer.Outdent();
419 writer.WriteLine("}");
420 }
421 writer.Outdent();
422 writer.WriteLine("}");
423 writer.Outdent();
424 writer.WriteLine("}");
425 writer.Outdent();
426 writer.WriteLine("}");
427 writer.WriteLine();
428 }
429
430 private void GenerateIsInitialized(TextGenerator writer) {
431 writer.WriteLine("public override bool IsInitialized {");
432 writer.Indent();
433 writer.WriteLine("get {");
434 writer.Indent();
435
436 // Check that all required fields in this message are set.
437 // TODO(kenton): We can optimize this when we switch to putting all the
438 // "has" fields into a single bitfield.
439 foreach (FieldDescriptor field in Descriptor.Fields) {
440 if (field.IsRequired) {
Jon Skeet4cf9e3c2008-11-24 11:11:28 +0000441 writer.WriteLine("if (!has{0}) return false;", field.CSharpOptions.PropertyName);
Jon Skeet68036862008-10-22 13:30:34 +0100442 }
443 }
444
445 // Now check that all embedded messages are initialized.
446 foreach (FieldDescriptor field in Descriptor.Fields) {
447 if (field.FieldType != FieldType.Message ||
448 !HasRequiredFields(field.MessageType, new Dictionary<MessageDescriptor, object>())) {
449 continue;
450 }
Jon Skeetd6343be2008-11-12 23:39:44 +0000451 string propertyName = NameHelpers.UnderscoresToPascalCase(GetFieldName(field));
Jon Skeet68036862008-10-22 13:30:34 +0100452 if (field.IsRepeated) {
Jon Skeetd6343be2008-11-12 23:39:44 +0000453 writer.WriteLine("foreach ({0} element in {1}List) {{", GetClassName(field.MessageType), propertyName);
Jon Skeet68036862008-10-22 13:30:34 +0100454 writer.WriteLine(" if (!element.IsInitialized) return false;");
455 writer.WriteLine("}");
456 } else if (field.IsOptional) {
457 writer.WriteLine("if (Has{0}) {{", propertyName);
458 writer.WriteLine(" if (!{0}.IsInitialized) return false;", propertyName);
459 writer.WriteLine("}");
460 } else {
461 writer.WriteLine("if (!{0}.IsInitialized) return false;", propertyName);
462 }
463 }
464
465 if (Descriptor.Proto.ExtensionRangeCount > 0) {
466 writer.WriteLine("if (!ExtensionsAreInitialized) return false;");
467 }
468 writer.WriteLine("return true;");
469 writer.Outdent();
470 writer.WriteLine("}");
471 writer.Outdent();
472 writer.WriteLine("}");
473 writer.WriteLine();
474 }
475 }
476}