blob: 3488cdc9436c5f1b9df3d4a01c5f4fba774cc033 [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();
Jon Skeet17ded822010-05-19 21:07:58 +0100196 // Make sure we've computed the serialized length, so that packed fields are generated correctly.
197 writer.WriteLine("int size = SerializedSize;");
Jon Skeet68036862008-10-22 13:30:34 +0100198 if (Descriptor.Proto.ExtensionRangeList.Count > 0) {
199 writer.WriteLine("pb::ExtendableMessage<{0}, {0}.Builder>.ExtensionWriter extensionWriter = CreateExtensionWriter(this);",
200 ClassName);
201 }
202
203 // Merge the fields and the extension ranges, both sorted by field number.
204 for (int i = 0, j = 0; i < Descriptor.Fields.Count || j < sortedExtensions.Count; ) {
205 if (i == Descriptor.Fields.Count) {
206 GenerateSerializeOneExtensionRange(writer, sortedExtensions[j++]);
207 } else if (j == sortedExtensions.Count) {
208 GenerateSerializeOneField(writer, sortedFields[i++]);
209 } else if (sortedFields[i].FieldNumber < sortedExtensions[j].Start) {
210 GenerateSerializeOneField(writer, sortedFields[i++]);
211 } else {
212 GenerateSerializeOneExtensionRange(writer, sortedExtensions[j++]);
213 }
214 }
215
216 if (Descriptor.Proto.Options.MessageSetWireFormat) {
217 writer.WriteLine("UnknownFields.WriteAsMessageSetTo(output);");
218 } else {
219 writer.WriteLine("UnknownFields.WriteTo(output);");
220 }
221
222 writer.Outdent();
223 writer.WriteLine("}");
224 writer.WriteLine();
225 writer.WriteLine("private int memoizedSerializedSize = -1;");
226 writer.WriteLine("public override int SerializedSize {");
227 writer.Indent();
228 writer.WriteLine("get {");
229 writer.Indent();
230 writer.WriteLine("int size = memoizedSerializedSize;");
231 writer.WriteLine("if (size != -1) return size;");
232 writer.WriteLine();
233 writer.WriteLine("size = 0;");
234 foreach (FieldDescriptor field in Descriptor.Fields) {
235 SourceGenerators.CreateFieldGenerator(field).GenerateSerializedSizeCode(writer);
236 }
237 if (Descriptor.Proto.ExtensionRangeCount > 0) {
238 writer.WriteLine("size += ExtensionsSerializedSize;");
239 }
240
241 if (Descriptor.Options.MessageSetWireFormat) {
242 writer.WriteLine("size += UnknownFields.SerializedSizeAsMessageSet;");
243 } else {
244 writer.WriteLine("size += UnknownFields.SerializedSize;");
245 }
246 writer.WriteLine("memoizedSerializedSize = size;");
247 writer.WriteLine("return size;");
248 writer.Outdent();
249 writer.WriteLine("}");
250 writer.Outdent();
251 writer.WriteLine("}");
252 writer.WriteLine();
253 }
254
Jon Skeet7ee85c42009-05-28 21:11:15 +0100255 private static void GenerateSerializeOneField(TextGenerator writer, FieldDescriptor fieldDescriptor) {
Jon Skeet68036862008-10-22 13:30:34 +0100256 SourceGenerators.CreateFieldGenerator(fieldDescriptor).GenerateSerializationCode(writer);
257 }
258
259 private static void GenerateSerializeOneExtensionRange(TextGenerator writer, ExtensionRange extensionRange) {
260 writer.WriteLine("extensionWriter.WriteUntil({0}, output);", extensionRange.End);
261 }
262
263 private void GenerateParseFromMethods(TextGenerator writer) {
264 // Note: These are separate from GenerateMessageSerializationMethods()
265 // because they need to be generated even for messages that are optimized
266 // for code size.
267
268 writer.WriteLine("public static {0} ParseFrom(pb::ByteString data) {{", ClassName);
269 writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed();");
270 writer.WriteLine("}");
271 writer.WriteLine("public static {0} ParseFrom(pb::ByteString data, pb::ExtensionRegistry extensionRegistry) {{", ClassName);
272 writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();");
273 writer.WriteLine("}");
274 writer.WriteLine("public static {0} ParseFrom(byte[] data) {{", ClassName);
275 writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed();");
276 writer.WriteLine("}");
277 writer.WriteLine("public static {0} ParseFrom(byte[] data, pb::ExtensionRegistry extensionRegistry) {{", ClassName);
278 writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();");
279 writer.WriteLine("}");
280 writer.WriteLine("public static {0} ParseFrom(global::System.IO.Stream input) {{", ClassName);
281 writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed();");
282 writer.WriteLine("}");
283 writer.WriteLine("public static {0} ParseFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) {{", ClassName);
284 writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();");
285 writer.WriteLine("}");
Jon Skeet2e6dc122009-05-29 06:34:52 +0100286 writer.WriteLine("public static {0} ParseDelimitedFrom(global::System.IO.Stream input) {{", ClassName);
287 writer.WriteLine(" return CreateBuilder().MergeDelimitedFrom(input).BuildParsed();");
288 writer.WriteLine("}");
289 writer.WriteLine("public static {0} ParseDelimitedFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) {{", ClassName);
290 writer.WriteLine(" return CreateBuilder().MergeDelimitedFrom(input, extensionRegistry).BuildParsed();");
291 writer.WriteLine("}");
Jon Skeet68036862008-10-22 13:30:34 +0100292 writer.WriteLine("public static {0} ParseFrom(pb::CodedInputStream input) {{", ClassName);
293 writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed();");
294 writer.WriteLine("}");
295 writer.WriteLine("public static {0} ParseFrom(pb::CodedInputStream input, pb::ExtensionRegistry extensionRegistry) {{", ClassName);
296 writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();");
297 writer.WriteLine("}");
298 }
299
300 /// <summary>
301 /// Returns whether or not the specified message type has any required fields.
302 /// If it doesn't, calls to check for initialization can be optimised.
303 /// TODO(jonskeet): Move this into MessageDescriptor?
304 /// </summary>
305 private static bool HasRequiredFields(MessageDescriptor descriptor, Dictionary<MessageDescriptor,object> alreadySeen) {
306 if (alreadySeen.ContainsKey(descriptor)) {
307 // The type is already in cache. This means that either:
308 // a. The type has no required fields.
309 // b. We are in the midst of checking if the type has required fields,
310 // somewhere up the stack. In this case, we know that if the type
311 // has any required fields, they'll be found when we return to it,
312 // and the whole call to HasRequiredFields() will return true.
313 // Therefore, we don't have to check if this type has required fields
314 // here.
315 return false;
316 }
317 alreadySeen[descriptor] = descriptor; // Value is irrelevant
318
319 // If the type has extensions, an extension with message type could contain
320 // required fields, so we have to be conservative and assume such an
321 // extension exists.
322 if (descriptor.Extensions.Count > 0) {
323 return true;
324 }
325
326 foreach (FieldDescriptor field in descriptor.Fields) {
327 if (field.IsRequired) {
328 return true;
329 }
330 // Message or group
331 if (field.MappedType == MappedType.Message) {
332 if (HasRequiredFields(field.MessageType, alreadySeen)) {
333 return true;
334 }
335 }
336 }
337 return false;
338 }
339
340 private void GenerateBuilder(TextGenerator writer) {
341 writer.WriteLine("public static Builder CreateBuilder() { return new Builder(); }");
Jon Skeete81a9d72009-02-24 16:50:56 +0000342 writer.WriteLine("public override Builder ToBuilder() { return CreateBuilder(this); }");
Jon Skeet68036862008-10-22 13:30:34 +0100343 writer.WriteLine("public override Builder CreateBuilderForType() { return new Builder(); }");
344 writer.WriteLine("public static Builder CreateBuilder({0} prototype) {{", ClassName);
345 writer.WriteLine(" return (Builder) new Builder().MergeFrom(prototype);");
346 writer.WriteLine("}");
347 writer.WriteLine();
348 writer.WriteLine("{0} sealed partial class Builder : pb::{2}Builder<{1}, Builder> {{",
349 ClassAccessLevel, ClassName, Descriptor.Proto.ExtensionRangeCount > 0 ? "Extendable" : "Generated");
350 writer.Indent();
351 writer.WriteLine("protected override Builder ThisBuilder {");
352 writer.WriteLine(" get { return this; }");
353 writer.WriteLine("}");
354 GenerateCommonBuilderMethods(writer);
355 if (Descriptor.File.Options.OptimizeFor == FileOptions.Types.OptimizeMode.SPEED) {
356 GenerateBuilderParsingMethods(writer);
357 }
358 foreach (FieldDescriptor field in Descriptor.Fields) {
359 writer.WriteLine();
360 // No field comment :(
361 SourceGenerators.CreateFieldGenerator(field).GenerateBuilderMembers(writer);
362 }
363 writer.Outdent();
364 writer.WriteLine("}");
Jon Skeet68036862008-10-22 13:30:34 +0100365 }
366
367 private void GenerateCommonBuilderMethods(TextGenerator writer) {
368 writer.WriteLine("{0} Builder() {{}}", ClassAccessLevel);
369 writer.WriteLine();
370 writer.WriteLine("{0} result = new {0}();", ClassName);
371 writer.WriteLine();
372 writer.WriteLine("protected override {0} MessageBeingBuilt {{", ClassName);
373 writer.WriteLine(" get { return result; }");
374 writer.WriteLine("}");
375 writer.WriteLine();
376 writer.WriteLine("public override Builder Clear() {");
377 writer.WriteLine(" result = new {0}();", ClassName);
378 writer.WriteLine(" return this;");
379 writer.WriteLine("}");
380 writer.WriteLine();
381 writer.WriteLine("public override Builder Clone() {");
382 writer.WriteLine(" return new Builder().MergeFrom(result);");
383 writer.WriteLine("}");
384 writer.WriteLine();
385 writer.WriteLine("public override pbd::MessageDescriptor DescriptorForType {");
Jon Skeet5cb5d782009-06-25 10:51:31 +0100386 writer.WriteLine(" get {{ return {0}.Descriptor; }}", FullClassName);
Jon Skeet68036862008-10-22 13:30:34 +0100387 writer.WriteLine("}");
388 writer.WriteLine();
389 writer.WriteLine("public override {0} DefaultInstanceForType {{", ClassName);
Jon Skeet5cb5d782009-06-25 10:51:31 +0100390 writer.WriteLine(" get {{ return {0}.DefaultInstance; }}", FullClassName);
Jon Skeet68036862008-10-22 13:30:34 +0100391 writer.WriteLine("}");
392 writer.WriteLine();
393
394 writer.WriteLine("public override {0} BuildPartial() {{", ClassName);
395 writer.Indent();
Jon Skeet2e6dc122009-05-29 06:34:52 +0100396 writer.WriteLine("if (result == null) {");
397 writer.WriteLine(" throw new global::System.InvalidOperationException(\"build() has already been called on this Builder\");");
398 writer.WriteLine("}");
Jon Skeet68036862008-10-22 13:30:34 +0100399 foreach (FieldDescriptor field in Descriptor.Fields) {
400 SourceGenerators.CreateFieldGenerator(field).GenerateBuildingCode(writer);
401 }
402 writer.WriteLine("{0} returnMe = result;", ClassName);
403 writer.WriteLine("result = null;");
404 writer.WriteLine("return returnMe;");
405 writer.Outdent();
406 writer.WriteLine("}");
407 writer.WriteLine();
408
409 if (Descriptor.File.Options.OptimizeFor == FileOptions.Types.OptimizeMode.SPEED) {
410 writer.WriteLine("public override Builder MergeFrom(pb::IMessage other) {");
411 writer.WriteLine(" if (other is {0}) {{", ClassName);
412 writer.WriteLine(" return MergeFrom(({0}) other);", ClassName);
413 writer.WriteLine(" } else {");
414 writer.WriteLine(" base.MergeFrom(other);");
415 writer.WriteLine(" return this;");
416 writer.WriteLine(" }");
417 writer.WriteLine("}");
418 writer.WriteLine();
419 writer.WriteLine("public override Builder MergeFrom({0} other) {{", ClassName);
420 // Optimization: If other is the default instance, we know none of its
421 // fields are set so we can skip the merge.
422 writer.Indent();
Jon Skeet5cb5d782009-06-25 10:51:31 +0100423 writer.WriteLine("if (other == {0}.DefaultInstance) return this;", FullClassName);
Jon Skeet68036862008-10-22 13:30:34 +0100424 foreach (FieldDescriptor field in Descriptor.Fields) {
425 SourceGenerators.CreateFieldGenerator(field).GenerateMergingCode(writer);
426 }
Jon Skeet49fcd4f2009-01-27 14:43:10 +0000427 // if message type has extensions
428 if (Descriptor.Proto.ExtensionRangeCount > 0) {
429 writer.WriteLine(" this.MergeExtensionFields(other);");
430 }
Jon Skeet68036862008-10-22 13:30:34 +0100431 writer.WriteLine("this.MergeUnknownFields(other.UnknownFields);");
432 writer.WriteLine("return this;");
433 writer.Outdent();
434 writer.WriteLine("}");
435 writer.WriteLine();
436 }
437 }
438
439 private void GenerateBuilderParsingMethods(TextGenerator writer) {
440 List<FieldDescriptor> sortedFields = new List<FieldDescriptor>(Descriptor.Fields);
441 sortedFields.Sort((f1, f2) => f1.FieldNumber.CompareTo(f2.FieldNumber));
442
443 writer.WriteLine("public override Builder MergeFrom(pb::CodedInputStream input) {");
444 writer.WriteLine(" return MergeFrom(input, pb::ExtensionRegistry.Empty);");
445 writer.WriteLine("}");
446 writer.WriteLine();
447 writer.WriteLine("public override Builder MergeFrom(pb::CodedInputStream input, pb::ExtensionRegistry extensionRegistry) {");
448 writer.Indent();
Jon Skeet7de1aef2009-03-05 14:23:17 +0000449 writer.WriteLine("pb::UnknownFieldSet.Builder unknownFields = null;");
Jon Skeet68036862008-10-22 13:30:34 +0100450 writer.WriteLine("while (true) {");
451 writer.Indent();
452 writer.WriteLine("uint tag = input.ReadTag();");
453 writer.WriteLine("switch (tag) {");
454 writer.Indent();
455 writer.WriteLine("case 0: {"); // 0 signals EOF / limit reached
Jon Skeet7de1aef2009-03-05 14:23:17 +0000456 writer.WriteLine(" if (unknownFields != null) {");
457 writer.WriteLine(" this.UnknownFields = unknownFields.Build();");
458 writer.WriteLine(" }");
Jon Skeet68036862008-10-22 13:30:34 +0100459 writer.WriteLine(" return this;");
460 writer.WriteLine("}");
461 writer.WriteLine("default: {");
Jon Skeet7de1aef2009-03-05 14:23:17 +0000462 writer.WriteLine(" if (pb::WireFormat.IsEndGroupTag(tag)) {");
463 writer.WriteLine(" if (unknownFields != null) {");
464 writer.WriteLine(" this.UnknownFields = unknownFields.Build();");
465 writer.WriteLine(" }");
Jon Skeet68036862008-10-22 13:30:34 +0100466 writer.WriteLine(" return this;"); // it's an endgroup tag
467 writer.WriteLine(" }");
Jon Skeet7de1aef2009-03-05 14:23:17 +0000468 writer.WriteLine(" if (unknownFields == null) {"); // First unknown field - create builder now
469 writer.WriteLine(" unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields);");
470 writer.WriteLine(" }");
471 writer.WriteLine(" ParseUnknownField(input, unknownFields, extensionRegistry, tag);");
Jon Skeet68036862008-10-22 13:30:34 +0100472 writer.WriteLine(" break;");
473 writer.WriteLine("}");
474 foreach (FieldDescriptor field in sortedFields) {
Jon Skeet25a28582009-02-18 16:06:22 +0000475 uint tag = WireFormat.MakeTag(field);
Jon Skeet68036862008-10-22 13:30:34 +0100476 writer.WriteLine("case {0}: {{", tag);
477 writer.Indent();
478 SourceGenerators.CreateFieldGenerator(field).GenerateParsingCode(writer);
479 writer.WriteLine("break;");
480 writer.Outdent();
481 writer.WriteLine("}");
482 }
483 writer.Outdent();
484 writer.WriteLine("}");
485 writer.Outdent();
486 writer.WriteLine("}");
487 writer.Outdent();
488 writer.WriteLine("}");
489 writer.WriteLine();
490 }
491
492 private void GenerateIsInitialized(TextGenerator writer) {
493 writer.WriteLine("public override bool IsInitialized {");
494 writer.Indent();
495 writer.WriteLine("get {");
496 writer.Indent();
497
498 // Check that all required fields in this message are set.
499 // TODO(kenton): We can optimize this when we switch to putting all the
500 // "has" fields into a single bitfield.
501 foreach (FieldDescriptor field in Descriptor.Fields) {
502 if (field.IsRequired) {
Jon Skeet4cf9e3c2008-11-24 11:11:28 +0000503 writer.WriteLine("if (!has{0}) return false;", field.CSharpOptions.PropertyName);
Jon Skeet68036862008-10-22 13:30:34 +0100504 }
505 }
506
507 // Now check that all embedded messages are initialized.
508 foreach (FieldDescriptor field in Descriptor.Fields) {
509 if (field.FieldType != FieldType.Message ||
510 !HasRequiredFields(field.MessageType, new Dictionary<MessageDescriptor, object>())) {
511 continue;
512 }
Jon Skeetd6343be2008-11-12 23:39:44 +0000513 string propertyName = NameHelpers.UnderscoresToPascalCase(GetFieldName(field));
Jon Skeet68036862008-10-22 13:30:34 +0100514 if (field.IsRepeated) {
Jon Skeetd6343be2008-11-12 23:39:44 +0000515 writer.WriteLine("foreach ({0} element in {1}List) {{", GetClassName(field.MessageType), propertyName);
Jon Skeet68036862008-10-22 13:30:34 +0100516 writer.WriteLine(" if (!element.IsInitialized) return false;");
517 writer.WriteLine("}");
518 } else if (field.IsOptional) {
519 writer.WriteLine("if (Has{0}) {{", propertyName);
520 writer.WriteLine(" if (!{0}.IsInitialized) return false;", propertyName);
521 writer.WriteLine("}");
522 } else {
523 writer.WriteLine("if (!{0}.IsInitialized) return false;", propertyName);
524 }
525 }
526
527 if (Descriptor.Proto.ExtensionRangeCount > 0) {
528 writer.WriteLine("if (!ExtensionsAreInitialized) return false;");
529 }
530 writer.WriteLine("return true;");
531 writer.Outdent();
532 writer.WriteLine("}");
533 writer.Outdent();
534 writer.WriteLine("}");
535 writer.WriteLine();
536 }
Jon Skeetdf67f142009-06-05 19:29:36 +0100537
538 internal void GenerateExtensionRegistrationCode(TextGenerator writer) {
539 foreach (FieldDescriptor extension in Descriptor.Extensions) {
540 new ExtensionGenerator(extension).GenerateExtensionRegistrationCode(writer);
541 }
542 foreach (MessageDescriptor nestedMessage in Descriptor.NestedTypes) {
543 new MessageGenerator(nestedMessage).GenerateExtensionRegistrationCode(writer);
544 }
545 }
Jon Skeet68036862008-10-22 13:30:34 +0100546 }
547}