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