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