blob: 9857764de8ca95ada5b755357efd7eebdc94f1b3 [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("}");
221 writer.WriteLine("public static {0} ParseFrom(pb::CodedInputStream input) {{", ClassName);
222 writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed();");
223 writer.WriteLine("}");
224 writer.WriteLine("public static {0} ParseFrom(pb::CodedInputStream input, pb::ExtensionRegistry extensionRegistry) {{", ClassName);
225 writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();");
226 writer.WriteLine("}");
227 }
228
229 /// <summary>
230 /// Returns whether or not the specified message type has any required fields.
231 /// If it doesn't, calls to check for initialization can be optimised.
232 /// TODO(jonskeet): Move this into MessageDescriptor?
233 /// </summary>
234 private static bool HasRequiredFields(MessageDescriptor descriptor, Dictionary<MessageDescriptor,object> alreadySeen) {
235 if (alreadySeen.ContainsKey(descriptor)) {
236 // The type is already in cache. This means that either:
237 // a. The type has no required fields.
238 // b. We are in the midst of checking if the type has required fields,
239 // somewhere up the stack. In this case, we know that if the type
240 // has any required fields, they'll be found when we return to it,
241 // and the whole call to HasRequiredFields() will return true.
242 // Therefore, we don't have to check if this type has required fields
243 // here.
244 return false;
245 }
246 alreadySeen[descriptor] = descriptor; // Value is irrelevant
247
248 // If the type has extensions, an extension with message type could contain
249 // required fields, so we have to be conservative and assume such an
250 // extension exists.
251 if (descriptor.Extensions.Count > 0) {
252 return true;
253 }
254
255 foreach (FieldDescriptor field in descriptor.Fields) {
256 if (field.IsRequired) {
257 return true;
258 }
259 // Message or group
260 if (field.MappedType == MappedType.Message) {
261 if (HasRequiredFields(field.MessageType, alreadySeen)) {
262 return true;
263 }
264 }
265 }
266 return false;
267 }
268
269 private void GenerateBuilder(TextGenerator writer) {
270 writer.WriteLine("public static Builder CreateBuilder() { return new Builder(); }");
Jon Skeete81a9d72009-02-24 16:50:56 +0000271 writer.WriteLine("public override Builder ToBuilder() { return CreateBuilder(this); }");
Jon Skeet68036862008-10-22 13:30:34 +0100272 writer.WriteLine("public override Builder CreateBuilderForType() { return new Builder(); }");
273 writer.WriteLine("public static Builder CreateBuilder({0} prototype) {{", ClassName);
274 writer.WriteLine(" return (Builder) new Builder().MergeFrom(prototype);");
275 writer.WriteLine("}");
276 writer.WriteLine();
277 writer.WriteLine("{0} sealed partial class Builder : pb::{2}Builder<{1}, Builder> {{",
278 ClassAccessLevel, ClassName, Descriptor.Proto.ExtensionRangeCount > 0 ? "Extendable" : "Generated");
279 writer.Indent();
280 writer.WriteLine("protected override Builder ThisBuilder {");
281 writer.WriteLine(" get { return this; }");
282 writer.WriteLine("}");
283 GenerateCommonBuilderMethods(writer);
284 if (Descriptor.File.Options.OptimizeFor == FileOptions.Types.OptimizeMode.SPEED) {
285 GenerateBuilderParsingMethods(writer);
286 }
287 foreach (FieldDescriptor field in Descriptor.Fields) {
288 writer.WriteLine();
289 // No field comment :(
290 SourceGenerators.CreateFieldGenerator(field).GenerateBuilderMembers(writer);
291 }
292 writer.Outdent();
293 writer.WriteLine("}");
294 writer.Outdent();
295 writer.WriteLine("}");
296 writer.WriteLine();
297 }
298
299 private void GenerateCommonBuilderMethods(TextGenerator writer) {
300 writer.WriteLine("{0} Builder() {{}}", ClassAccessLevel);
301 writer.WriteLine();
302 writer.WriteLine("{0} result = new {0}();", ClassName);
303 writer.WriteLine();
304 writer.WriteLine("protected override {0} MessageBeingBuilt {{", ClassName);
305 writer.WriteLine(" get { return result; }");
306 writer.WriteLine("}");
307 writer.WriteLine();
308 writer.WriteLine("public override Builder Clear() {");
309 writer.WriteLine(" result = new {0}();", ClassName);
310 writer.WriteLine(" return this;");
311 writer.WriteLine("}");
312 writer.WriteLine();
313 writer.WriteLine("public override Builder Clone() {");
314 writer.WriteLine(" return new Builder().MergeFrom(result);");
315 writer.WriteLine("}");
316 writer.WriteLine();
317 writer.WriteLine("public override pbd::MessageDescriptor DescriptorForType {");
318 writer.WriteLine(" get {{ return {0}.Descriptor; }}", ClassName);
319 writer.WriteLine("}");
320 writer.WriteLine();
321 writer.WriteLine("public override {0} DefaultInstanceForType {{", ClassName);
322 writer.WriteLine(" get {{ return {0}.DefaultInstance; }}", ClassName);
323 writer.WriteLine("}");
324 writer.WriteLine();
325
326 writer.WriteLine("public override {0} BuildPartial() {{", ClassName);
327 writer.Indent();
328 foreach (FieldDescriptor field in Descriptor.Fields) {
329 SourceGenerators.CreateFieldGenerator(field).GenerateBuildingCode(writer);
330 }
331 writer.WriteLine("{0} returnMe = result;", ClassName);
332 writer.WriteLine("result = null;");
333 writer.WriteLine("return returnMe;");
334 writer.Outdent();
335 writer.WriteLine("}");
336 writer.WriteLine();
337
338 if (Descriptor.File.Options.OptimizeFor == FileOptions.Types.OptimizeMode.SPEED) {
339 writer.WriteLine("public override Builder MergeFrom(pb::IMessage other) {");
340 writer.WriteLine(" if (other is {0}) {{", ClassName);
341 writer.WriteLine(" return MergeFrom(({0}) other);", ClassName);
342 writer.WriteLine(" } else {");
343 writer.WriteLine(" base.MergeFrom(other);");
344 writer.WriteLine(" return this;");
345 writer.WriteLine(" }");
346 writer.WriteLine("}");
347 writer.WriteLine();
348 writer.WriteLine("public override Builder MergeFrom({0} other) {{", ClassName);
349 // Optimization: If other is the default instance, we know none of its
350 // fields are set so we can skip the merge.
351 writer.Indent();
352 writer.WriteLine("if (other == {0}.DefaultInstance) return this;", ClassName);
353 foreach (FieldDescriptor field in Descriptor.Fields) {
354 SourceGenerators.CreateFieldGenerator(field).GenerateMergingCode(writer);
355 }
Jon Skeet49fcd4f2009-01-27 14:43:10 +0000356 // if message type has extensions
357 if (Descriptor.Proto.ExtensionRangeCount > 0) {
358 writer.WriteLine(" this.MergeExtensionFields(other);");
359 }
Jon Skeet68036862008-10-22 13:30:34 +0100360 writer.WriteLine("this.MergeUnknownFields(other.UnknownFields);");
361 writer.WriteLine("return this;");
362 writer.Outdent();
363 writer.WriteLine("}");
364 writer.WriteLine();
365 }
366 }
367
368 private void GenerateBuilderParsingMethods(TextGenerator writer) {
369 List<FieldDescriptor> sortedFields = new List<FieldDescriptor>(Descriptor.Fields);
370 sortedFields.Sort((f1, f2) => f1.FieldNumber.CompareTo(f2.FieldNumber));
371
372 writer.WriteLine("public override Builder MergeFrom(pb::CodedInputStream input) {");
373 writer.WriteLine(" return MergeFrom(input, pb::ExtensionRegistry.Empty);");
374 writer.WriteLine("}");
375 writer.WriteLine();
376 writer.WriteLine("public override Builder MergeFrom(pb::CodedInputStream input, pb::ExtensionRegistry extensionRegistry) {");
377 writer.Indent();
Jon Skeet7de1aef2009-03-05 14:23:17 +0000378 writer.WriteLine("pb::UnknownFieldSet.Builder unknownFields = null;");
Jon Skeet68036862008-10-22 13:30:34 +0100379 writer.WriteLine("while (true) {");
380 writer.Indent();
381 writer.WriteLine("uint tag = input.ReadTag();");
382 writer.WriteLine("switch (tag) {");
383 writer.Indent();
384 writer.WriteLine("case 0: {"); // 0 signals EOF / limit reached
Jon Skeet7de1aef2009-03-05 14:23:17 +0000385 writer.WriteLine(" if (unknownFields != null) {");
386 writer.WriteLine(" this.UnknownFields = unknownFields.Build();");
387 writer.WriteLine(" }");
Jon Skeet68036862008-10-22 13:30:34 +0100388 writer.WriteLine(" return this;");
389 writer.WriteLine("}");
390 writer.WriteLine("default: {");
Jon Skeet7de1aef2009-03-05 14:23:17 +0000391 writer.WriteLine(" if (pb::WireFormat.IsEndGroupTag(tag)) {");
392 writer.WriteLine(" if (unknownFields != null) {");
393 writer.WriteLine(" this.UnknownFields = unknownFields.Build();");
394 writer.WriteLine(" }");
Jon Skeet68036862008-10-22 13:30:34 +0100395 writer.WriteLine(" return this;"); // it's an endgroup tag
396 writer.WriteLine(" }");
Jon Skeet7de1aef2009-03-05 14:23:17 +0000397 writer.WriteLine(" if (unknownFields == null) {"); // First unknown field - create builder now
398 writer.WriteLine(" unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields);");
399 writer.WriteLine(" }");
400 writer.WriteLine(" ParseUnknownField(input, unknownFields, extensionRegistry, tag);");
Jon Skeet68036862008-10-22 13:30:34 +0100401 writer.WriteLine(" break;");
402 writer.WriteLine("}");
403 foreach (FieldDescriptor field in sortedFields) {
Jon Skeet25a28582009-02-18 16:06:22 +0000404 uint tag = WireFormat.MakeTag(field);
Jon Skeet68036862008-10-22 13:30:34 +0100405 writer.WriteLine("case {0}: {{", tag);
406 writer.Indent();
407 SourceGenerators.CreateFieldGenerator(field).GenerateParsingCode(writer);
408 writer.WriteLine("break;");
409 writer.Outdent();
410 writer.WriteLine("}");
411 }
412 writer.Outdent();
413 writer.WriteLine("}");
414 writer.Outdent();
415 writer.WriteLine("}");
416 writer.Outdent();
417 writer.WriteLine("}");
418 writer.WriteLine();
419 }
420
421 private void GenerateIsInitialized(TextGenerator writer) {
422 writer.WriteLine("public override bool IsInitialized {");
423 writer.Indent();
424 writer.WriteLine("get {");
425 writer.Indent();
426
427 // Check that all required fields in this message are set.
428 // TODO(kenton): We can optimize this when we switch to putting all the
429 // "has" fields into a single bitfield.
430 foreach (FieldDescriptor field in Descriptor.Fields) {
431 if (field.IsRequired) {
Jon Skeet4cf9e3c2008-11-24 11:11:28 +0000432 writer.WriteLine("if (!has{0}) return false;", field.CSharpOptions.PropertyName);
Jon Skeet68036862008-10-22 13:30:34 +0100433 }
434 }
435
436 // Now check that all embedded messages are initialized.
437 foreach (FieldDescriptor field in Descriptor.Fields) {
438 if (field.FieldType != FieldType.Message ||
439 !HasRequiredFields(field.MessageType, new Dictionary<MessageDescriptor, object>())) {
440 continue;
441 }
Jon Skeetd6343be2008-11-12 23:39:44 +0000442 string propertyName = NameHelpers.UnderscoresToPascalCase(GetFieldName(field));
Jon Skeet68036862008-10-22 13:30:34 +0100443 if (field.IsRepeated) {
Jon Skeetd6343be2008-11-12 23:39:44 +0000444 writer.WriteLine("foreach ({0} element in {1}List) {{", GetClassName(field.MessageType), propertyName);
Jon Skeet68036862008-10-22 13:30:34 +0100445 writer.WriteLine(" if (!element.IsInitialized) return false;");
446 writer.WriteLine("}");
447 } else if (field.IsOptional) {
448 writer.WriteLine("if (Has{0}) {{", propertyName);
449 writer.WriteLine(" if (!{0}.IsInitialized) return false;", propertyName);
450 writer.WriteLine("}");
451 } else {
452 writer.WriteLine("if (!{0}.IsInitialized) return false;", propertyName);
453 }
454 }
455
456 if (Descriptor.Proto.ExtensionRangeCount > 0) {
457 writer.WriteLine("if (!ExtensionsAreInitialized) return false;");
458 }
459 writer.WriteLine("return true;");
460 writer.Outdent();
461 writer.WriteLine("}");
462 writer.Outdent();
463 writer.WriteLine("}");
464 writer.WriteLine();
465 }
466 }
467}