|
|
|
@ -0,0 +1,453 @@ |
|
|
|
|
using System.Collections; |
|
|
|
|
using Google.ProtocolBuffers.Descriptors; |
|
|
|
|
using Google.ProtocolBuffers.DescriptorProtos; |
|
|
|
|
using System.Collections.Generic; |
|
|
|
|
|
|
|
|
|
using ExtensionRange = Google.ProtocolBuffers.DescriptorProtos.DescriptorProto.Types.ExtensionRange; |
|
|
|
|
|
|
|
|
|
namespace Google.ProtocolBuffers.ProtoGen { |
|
|
|
|
internal class MessageGenerator : SourceGeneratorBase<MessageDescriptor>, ISourceGenerator { |
|
|
|
|
internal MessageGenerator(MessageDescriptor descriptor) : base(descriptor) { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private string ClassName { |
|
|
|
|
get { return Descriptor.Name; } |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private string FullClassName { |
|
|
|
|
get { return DescriptorUtil.GetClassName(Descriptor); } |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary> |
|
|
|
|
/// Get an identifier that uniquely identifies this type within the file. |
|
|
|
|
/// This is used to declare static variables related to this type at the |
|
|
|
|
/// outermost file scope. |
|
|
|
|
/// </summary> |
|
|
|
|
static string GetUniqueFileScopeIdentifier(IDescriptor descriptor) { |
|
|
|
|
return "static_" + descriptor.FullName.Replace(".", "_"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
internal void GenerateStaticVariables(TextGenerator writer) { |
|
|
|
|
// Because descriptor.proto (Google.ProtocolBuffers.DescriptorProtos) is |
|
|
|
|
// used in the construction of descriptors, we have a tricky bootstrapping |
|
|
|
|
// problem. To help control static initialization order, we make sure all |
|
|
|
|
// descriptors and other static data that depends on them are members of |
|
|
|
|
// the proto-descriptor class. This way, they will be initialized in |
|
|
|
|
// a deterministic order. |
|
|
|
|
|
|
|
|
|
string identifier = GetUniqueFileScopeIdentifier(Descriptor); |
|
|
|
|
|
|
|
|
|
// The descriptor for this type. |
|
|
|
|
string access = Descriptor.File.Options.GetExtension(CSharpOptions.CSharpNestClasses) ? "private" : "internal"; |
|
|
|
|
writer.WriteLine("{0} static readonly pbd::MessageDescriptor internal__{1}__Descriptor", access, identifier); |
|
|
|
|
if (Descriptor.ContainingType == null) { |
|
|
|
|
writer.WriteLine(" = Descriptor.MessageTypes[{0}];", Descriptor.Index); |
|
|
|
|
} else { |
|
|
|
|
writer.WriteLine(" = internal__{0}__Descriptor.NestedTypes[{1}];", GetUniqueFileScopeIdentifier(Descriptor.ContainingType), Descriptor.Index); |
|
|
|
|
} |
|
|
|
|
writer.WriteLine("{0} static pb::FieldAccess.FieldAccessorTable<{1}, {1}.Builder> internal__{2}__FieldAccessorTable", |
|
|
|
|
access, FullClassName, identifier); |
|
|
|
|
writer.WriteLine(" = new pb::FieldAccess.FieldAccessorTable<{0}, {0}.Builder>(internal__{1}__Descriptor,", |
|
|
|
|
FullClassName, identifier); |
|
|
|
|
writer.Print(" new string[] { "); |
|
|
|
|
foreach (FieldDescriptor field in Descriptor.Fields) { |
|
|
|
|
writer.Write("\"{0}\", ", Helpers.UnderscoresToPascalCase(DescriptorUtil.GetFieldName(field))); |
|
|
|
|
} |
|
|
|
|
writer.WriteLine("});"); |
|
|
|
|
|
|
|
|
|
// Generate static members for all nested types. |
|
|
|
|
foreach (MessageDescriptor nestedMessage in Descriptor.NestedTypes) { |
|
|
|
|
new MessageGenerator(nestedMessage).GenerateStaticVariables(writer); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public void Generate(TextGenerator writer) { |
|
|
|
|
writer.WriteLine("{0} sealed partial class {1} : pb::{2}Message<{1}, {1}.Builder> {{", |
|
|
|
|
ClassAccessLevel, ClassName, Descriptor.Proto.ExtensionRangeCount > 0 ? "Extendable" : "Generated"); |
|
|
|
|
writer.Indent(); |
|
|
|
|
// Must call BuildPartial() to make sure all lists are made read-only |
|
|
|
|
writer.WriteLine("private static readonly {0} defaultInstance = new Builder().BuildPartial();", ClassName); |
|
|
|
|
writer.WriteLine("public static {0} DefaultInstance {{", ClassName); |
|
|
|
|
writer.WriteLine(" get { return defaultInstance; }"); |
|
|
|
|
writer.WriteLine("}"); |
|
|
|
|
writer.WriteLine(); |
|
|
|
|
writer.WriteLine("public override {0} DefaultInstanceForType {{", ClassName); |
|
|
|
|
writer.WriteLine(" get { return defaultInstance; }"); |
|
|
|
|
writer.WriteLine("}"); |
|
|
|
|
writer.WriteLine(); |
|
|
|
|
writer.WriteLine("protected override {0} ThisMessage {{", ClassName); |
|
|
|
|
writer.WriteLine(" get { return this; }"); |
|
|
|
|
writer.WriteLine("}"); |
|
|
|
|
writer.WriteLine(); |
|
|
|
|
writer.WriteLine("public static pbd::MessageDescriptor Descriptor {"); |
|
|
|
|
writer.WriteLine(" get {{ return {0}.internal__{1}__Descriptor; }}", DescriptorUtil.GetFullUmbrellaClassName(Descriptor.File), |
|
|
|
|
GetUniqueFileScopeIdentifier(Descriptor)); |
|
|
|
|
writer.WriteLine("}"); |
|
|
|
|
writer.WriteLine(); |
|
|
|
|
writer.WriteLine("protected override pb::FieldAccess.FieldAccessorTable<{0}, {0}.Builder> InternalFieldAccessors {{", ClassName); |
|
|
|
|
writer.WriteLine(" get {{ return {0}.internal__{1}__FieldAccessorTable; }}", DescriptorUtil.GetFullUmbrellaClassName(Descriptor.File), |
|
|
|
|
GetUniqueFileScopeIdentifier(Descriptor)); |
|
|
|
|
writer.WriteLine("}"); |
|
|
|
|
writer.WriteLine(); |
|
|
|
|
|
|
|
|
|
// Extensions don't need to go in an extra nested type |
|
|
|
|
WriteChildren(writer, null, Descriptor.Extensions); |
|
|
|
|
|
|
|
|
|
if (Descriptor.EnumTypes.Count + Descriptor.NestedTypes.Count > 0) { |
|
|
|
|
writer.WriteLine("#region Nested types"); |
|
|
|
|
writer.WriteLine("public static class Types {"); |
|
|
|
|
writer.Indent(); |
|
|
|
|
WriteChildren(writer, null, Descriptor.EnumTypes); |
|
|
|
|
WriteChildren(writer, null, Descriptor.NestedTypes); |
|
|
|
|
writer.Outdent(); |
|
|
|
|
writer.WriteLine("}"); |
|
|
|
|
writer.WriteLine("#endregion"); |
|
|
|
|
writer.WriteLine(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
foreach(FieldDescriptor fieldDescriptor in Descriptor.Fields) { |
|
|
|
|
// Rats: we lose the debug comment here :( |
|
|
|
|
SourceGenerators.CreateFieldGenerator(fieldDescriptor).GenerateMembers(writer); |
|
|
|
|
writer.WriteLine(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (Descriptor.File.Options.OptimizeFor == FileOptions.Types.OptimizeMode.SPEED) { |
|
|
|
|
GenerateIsInitialized(writer); |
|
|
|
|
GenerateMessageSerializationMethods(writer); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
GenerateParseFromMethods(writer); |
|
|
|
|
GenerateBuilder(writer); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void GenerateMessageSerializationMethods(TextGenerator writer) { |
|
|
|
|
List<FieldDescriptor> sortedFields = new List<FieldDescriptor>(Descriptor.Fields); |
|
|
|
|
sortedFields.Sort((f1, f2) => f1.FieldNumber.CompareTo(f2.FieldNumber)); |
|
|
|
|
|
|
|
|
|
List<ExtensionRange> sortedExtensions = new List<ExtensionRange>(Descriptor.Proto.ExtensionRangeList); |
|
|
|
|
sortedExtensions.Sort((r1, r2) => (r1.Start.CompareTo(r2.Start))); |
|
|
|
|
|
|
|
|
|
writer.WriteLine("public override void WriteTo(pb::CodedOutputStream output) {"); |
|
|
|
|
writer.Indent(); |
|
|
|
|
if (Descriptor.Proto.ExtensionRangeList.Count > 0) { |
|
|
|
|
writer.WriteLine("pb::ExtendableMessage<{0}, {0}.Builder>.ExtensionWriter extensionWriter = CreateExtensionWriter(this);", |
|
|
|
|
ClassName); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Merge the fields and the extension ranges, both sorted by field number. |
|
|
|
|
for (int i = 0, j = 0; i < Descriptor.Fields.Count || j < sortedExtensions.Count; ) { |
|
|
|
|
if (i == Descriptor.Fields.Count) { |
|
|
|
|
GenerateSerializeOneExtensionRange(writer, sortedExtensions[j++]); |
|
|
|
|
} else if (j == sortedExtensions.Count) { |
|
|
|
|
GenerateSerializeOneField(writer, sortedFields[i++]); |
|
|
|
|
} else if (sortedFields[i].FieldNumber < sortedExtensions[j].Start) { |
|
|
|
|
GenerateSerializeOneField(writer, sortedFields[i++]); |
|
|
|
|
} else { |
|
|
|
|
GenerateSerializeOneExtensionRange(writer, sortedExtensions[j++]); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (Descriptor.Proto.Options.MessageSetWireFormat) { |
|
|
|
|
writer.WriteLine("UnknownFields.WriteAsMessageSetTo(output);"); |
|
|
|
|
} else { |
|
|
|
|
writer.WriteLine("UnknownFields.WriteTo(output);"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
writer.Outdent(); |
|
|
|
|
writer.WriteLine("}"); |
|
|
|
|
writer.WriteLine(); |
|
|
|
|
writer.WriteLine("private int memoizedSerializedSize = -1;"); |
|
|
|
|
writer.WriteLine("public override int SerializedSize {"); |
|
|
|
|
writer.Indent(); |
|
|
|
|
writer.WriteLine("get {"); |
|
|
|
|
writer.Indent(); |
|
|
|
|
writer.WriteLine("int size = memoizedSerializedSize;"); |
|
|
|
|
writer.WriteLine("if (size != -1) return size;"); |
|
|
|
|
writer.WriteLine(); |
|
|
|
|
writer.WriteLine("size = 0;"); |
|
|
|
|
foreach (FieldDescriptor field in Descriptor.Fields) { |
|
|
|
|
SourceGenerators.CreateFieldGenerator(field).GenerateSerializedSizeCode(writer); |
|
|
|
|
} |
|
|
|
|
if (Descriptor.Proto.ExtensionRangeCount > 0) { |
|
|
|
|
writer.WriteLine("size += ExtensionsSerializedSize;"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (Descriptor.Options.MessageSetWireFormat) { |
|
|
|
|
writer.WriteLine("size += UnknownFields.SerializedSizeAsMessageSet;"); |
|
|
|
|
} else { |
|
|
|
|
writer.WriteLine("size += UnknownFields.SerializedSize;"); |
|
|
|
|
} |
|
|
|
|
writer.WriteLine("memoizedSerializedSize = size;"); |
|
|
|
|
writer.WriteLine("return size;"); |
|
|
|
|
writer.Outdent(); |
|
|
|
|
writer.WriteLine("}"); |
|
|
|
|
writer.Outdent(); |
|
|
|
|
writer.WriteLine("}"); |
|
|
|
|
writer.WriteLine(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static void GenerateSerializeOneField(TextGenerator writer, FieldDescriptor fieldDescriptor) { |
|
|
|
|
SourceGenerators.CreateFieldGenerator(fieldDescriptor).GenerateSerializationCode(writer); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static void GenerateSerializeOneExtensionRange(TextGenerator writer, ExtensionRange extensionRange) { |
|
|
|
|
writer.WriteLine("extensionWriter.WriteUntil({0}, output);", extensionRange.End); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void GenerateParseFromMethods(TextGenerator writer) { |
|
|
|
|
// Note: These are separate from GenerateMessageSerializationMethods() |
|
|
|
|
// because they need to be generated even for messages that are optimized |
|
|
|
|
// for code size. |
|
|
|
|
|
|
|
|
|
writer.WriteLine("public static {0} ParseFrom(pb::ByteString data) {{", ClassName); |
|
|
|
|
writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed();"); |
|
|
|
|
writer.WriteLine("}"); |
|
|
|
|
writer.WriteLine("public static {0} ParseFrom(pb::ByteString data, pb::ExtensionRegistry extensionRegistry) {{", ClassName); |
|
|
|
|
writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();"); |
|
|
|
|
writer.WriteLine("}"); |
|
|
|
|
writer.WriteLine("public static {0} ParseFrom(byte[] data) {{", ClassName); |
|
|
|
|
writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed();"); |
|
|
|
|
writer.WriteLine("}"); |
|
|
|
|
writer.WriteLine("public static {0} ParseFrom(byte[] data, pb::ExtensionRegistry extensionRegistry) {{", ClassName); |
|
|
|
|
writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();"); |
|
|
|
|
writer.WriteLine("}"); |
|
|
|
|
writer.WriteLine("public static {0} ParseFrom(global::System.IO.Stream input) {{", ClassName); |
|
|
|
|
writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed();"); |
|
|
|
|
writer.WriteLine("}"); |
|
|
|
|
writer.WriteLine("public static {0} ParseFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) {{", ClassName); |
|
|
|
|
writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();"); |
|
|
|
|
writer.WriteLine("}"); |
|
|
|
|
writer.WriteLine("public static {0} ParseFrom(pb::CodedInputStream input) {{", ClassName); |
|
|
|
|
writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed();"); |
|
|
|
|
writer.WriteLine("}"); |
|
|
|
|
writer.WriteLine("public static {0} ParseFrom(pb::CodedInputStream input, pb::ExtensionRegistry extensionRegistry) {{", ClassName); |
|
|
|
|
writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();"); |
|
|
|
|
writer.WriteLine("}"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary> |
|
|
|
|
/// Returns whether or not the specified message type has any required fields. |
|
|
|
|
/// If it doesn't, calls to check for initialization can be optimised. |
|
|
|
|
/// TODO(jonskeet): Move this into MessageDescriptor? |
|
|
|
|
/// </summary> |
|
|
|
|
private static bool HasRequiredFields(MessageDescriptor descriptor, Dictionary<MessageDescriptor,object> alreadySeen) { |
|
|
|
|
if (alreadySeen.ContainsKey(descriptor)) { |
|
|
|
|
// The type is already in cache. This means that either: |
|
|
|
|
// a. The type has no required fields. |
|
|
|
|
// b. We are in the midst of checking if the type has required fields, |
|
|
|
|
// somewhere up the stack. In this case, we know that if the type |
|
|
|
|
// has any required fields, they'll be found when we return to it, |
|
|
|
|
// and the whole call to HasRequiredFields() will return true. |
|
|
|
|
// Therefore, we don't have to check if this type has required fields |
|
|
|
|
// here. |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
alreadySeen[descriptor] = descriptor; // Value is irrelevant |
|
|
|
|
|
|
|
|
|
// If the type has extensions, an extension with message type could contain |
|
|
|
|
// required fields, so we have to be conservative and assume such an |
|
|
|
|
// extension exists. |
|
|
|
|
if (descriptor.Extensions.Count > 0) { |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
foreach (FieldDescriptor field in descriptor.Fields) { |
|
|
|
|
if (field.IsRequired) { |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
// Message or group |
|
|
|
|
if (field.MappedType == MappedType.Message) { |
|
|
|
|
if (HasRequiredFields(field.MessageType, alreadySeen)) { |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void GenerateBuilder(TextGenerator writer) { |
|
|
|
|
writer.WriteLine("public static Builder CreateBuilder() { return new Builder(); }"); |
|
|
|
|
writer.WriteLine("public override Builder CreateBuilderForType() { return new Builder(); }"); |
|
|
|
|
writer.WriteLine("public static Builder CreateBuilder({0} prototype) {{", ClassName); |
|
|
|
|
writer.WriteLine(" return (Builder) new Builder().MergeFrom(prototype);"); |
|
|
|
|
writer.WriteLine("}"); |
|
|
|
|
writer.WriteLine(); |
|
|
|
|
writer.WriteLine("{0} sealed partial class Builder : pb::{2}Builder<{1}, Builder> {{", |
|
|
|
|
ClassAccessLevel, ClassName, Descriptor.Proto.ExtensionRangeCount > 0 ? "Extendable" : "Generated"); |
|
|
|
|
writer.Indent(); |
|
|
|
|
writer.WriteLine("protected override Builder ThisBuilder {"); |
|
|
|
|
writer.WriteLine(" get { return this; }"); |
|
|
|
|
writer.WriteLine("}"); |
|
|
|
|
GenerateCommonBuilderMethods(writer); |
|
|
|
|
if (Descriptor.File.Options.OptimizeFor == FileOptions.Types.OptimizeMode.SPEED) { |
|
|
|
|
GenerateBuilderParsingMethods(writer); |
|
|
|
|
} |
|
|
|
|
foreach (FieldDescriptor field in Descriptor.Fields) { |
|
|
|
|
writer.WriteLine(); |
|
|
|
|
// No field comment :( |
|
|
|
|
SourceGenerators.CreateFieldGenerator(field).GenerateBuilderMembers(writer); |
|
|
|
|
} |
|
|
|
|
writer.Outdent(); |
|
|
|
|
writer.WriteLine("}"); |
|
|
|
|
writer.Outdent(); |
|
|
|
|
writer.WriteLine("}"); |
|
|
|
|
writer.WriteLine(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void GenerateCommonBuilderMethods(TextGenerator writer) { |
|
|
|
|
writer.WriteLine("{0} Builder() {{}}", ClassAccessLevel); |
|
|
|
|
writer.WriteLine(); |
|
|
|
|
writer.WriteLine("{0} result = new {0}();", ClassName); |
|
|
|
|
writer.WriteLine(); |
|
|
|
|
writer.WriteLine("protected override {0} MessageBeingBuilt {{", ClassName); |
|
|
|
|
writer.WriteLine(" get { return result; }"); |
|
|
|
|
writer.WriteLine("}"); |
|
|
|
|
writer.WriteLine(); |
|
|
|
|
writer.WriteLine("public override Builder Clear() {"); |
|
|
|
|
writer.WriteLine(" result = new {0}();", ClassName); |
|
|
|
|
writer.WriteLine(" return this;"); |
|
|
|
|
writer.WriteLine("}"); |
|
|
|
|
writer.WriteLine(); |
|
|
|
|
writer.WriteLine("public override Builder Clone() {"); |
|
|
|
|
writer.WriteLine(" return new Builder().MergeFrom(result);"); |
|
|
|
|
writer.WriteLine("}"); |
|
|
|
|
writer.WriteLine(); |
|
|
|
|
writer.WriteLine("public override pbd::MessageDescriptor DescriptorForType {"); |
|
|
|
|
writer.WriteLine(" get {{ return {0}.Descriptor; }}", ClassName); |
|
|
|
|
writer.WriteLine("}"); |
|
|
|
|
writer.WriteLine(); |
|
|
|
|
writer.WriteLine("public override {0} DefaultInstanceForType {{", ClassName); |
|
|
|
|
writer.WriteLine(" get {{ return {0}.DefaultInstance; }}", ClassName); |
|
|
|
|
writer.WriteLine("}"); |
|
|
|
|
writer.WriteLine(); |
|
|
|
|
|
|
|
|
|
writer.WriteLine("public override {0} BuildPartial() {{", ClassName); |
|
|
|
|
writer.Indent(); |
|
|
|
|
foreach (FieldDescriptor field in Descriptor.Fields) { |
|
|
|
|
SourceGenerators.CreateFieldGenerator(field).GenerateBuildingCode(writer); |
|
|
|
|
} |
|
|
|
|
writer.WriteLine("{0} returnMe = result;", ClassName); |
|
|
|
|
writer.WriteLine("result = null;"); |
|
|
|
|
writer.WriteLine("return returnMe;"); |
|
|
|
|
writer.Outdent(); |
|
|
|
|
writer.WriteLine("}"); |
|
|
|
|
writer.WriteLine(); |
|
|
|
|
|
|
|
|
|
if (Descriptor.File.Options.OptimizeFor == FileOptions.Types.OptimizeMode.SPEED) { |
|
|
|
|
writer.WriteLine("public override Builder MergeFrom(pb::IMessage other) {"); |
|
|
|
|
writer.WriteLine(" if (other is {0}) {{", ClassName); |
|
|
|
|
writer.WriteLine(" return MergeFrom(({0}) other);", ClassName); |
|
|
|
|
writer.WriteLine(" } else {"); |
|
|
|
|
writer.WriteLine(" base.MergeFrom(other);"); |
|
|
|
|
writer.WriteLine(" return this;"); |
|
|
|
|
writer.WriteLine(" }"); |
|
|
|
|
writer.WriteLine("}"); |
|
|
|
|
writer.WriteLine(); |
|
|
|
|
writer.WriteLine("public override Builder MergeFrom({0} other) {{", ClassName); |
|
|
|
|
// Optimization: If other is the default instance, we know none of its |
|
|
|
|
// fields are set so we can skip the merge. |
|
|
|
|
writer.Indent(); |
|
|
|
|
writer.WriteLine("if (other == {0}.DefaultInstance) return this;", ClassName); |
|
|
|
|
foreach (FieldDescriptor field in Descriptor.Fields) { |
|
|
|
|
SourceGenerators.CreateFieldGenerator(field).GenerateMergingCode(writer); |
|
|
|
|
} |
|
|
|
|
writer.WriteLine("this.MergeUnknownFields(other.UnknownFields);"); |
|
|
|
|
writer.WriteLine("return this;"); |
|
|
|
|
writer.Outdent(); |
|
|
|
|
writer.WriteLine("}"); |
|
|
|
|
writer.WriteLine(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void GenerateBuilderParsingMethods(TextGenerator writer) { |
|
|
|
|
List<FieldDescriptor> sortedFields = new List<FieldDescriptor>(Descriptor.Fields); |
|
|
|
|
sortedFields.Sort((f1, f2) => f1.FieldNumber.CompareTo(f2.FieldNumber)); |
|
|
|
|
|
|
|
|
|
writer.WriteLine("public override Builder MergeFrom(pb::CodedInputStream input) {"); |
|
|
|
|
writer.WriteLine(" return MergeFrom(input, pb::ExtensionRegistry.Empty);"); |
|
|
|
|
writer.WriteLine("}"); |
|
|
|
|
writer.WriteLine(); |
|
|
|
|
writer.WriteLine("public override Builder MergeFrom(pb::CodedInputStream input, pb::ExtensionRegistry extensionRegistry) {"); |
|
|
|
|
writer.Indent(); |
|
|
|
|
writer.WriteLine("pb::UnknownFieldSet.Builder unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields);"); |
|
|
|
|
writer.WriteLine("while (true) {"); |
|
|
|
|
writer.Indent(); |
|
|
|
|
writer.WriteLine("uint tag = input.ReadTag();"); |
|
|
|
|
writer.WriteLine("switch (tag) {"); |
|
|
|
|
writer.Indent(); |
|
|
|
|
writer.WriteLine("case 0: {"); // 0 signals EOF / limit reached |
|
|
|
|
writer.WriteLine(" this.UnknownFields = unknownFields.Build();"); |
|
|
|
|
writer.WriteLine(" return this;"); |
|
|
|
|
writer.WriteLine("}"); |
|
|
|
|
writer.WriteLine("default: {"); |
|
|
|
|
writer.WriteLine(" if (!ParseUnknownField(input, unknownFields, extensionRegistry, tag)) {"); |
|
|
|
|
writer.WriteLine(" this.UnknownFields = unknownFields.Build();"); |
|
|
|
|
writer.WriteLine(" return this;"); // it's an endgroup tag |
|
|
|
|
writer.WriteLine(" }"); |
|
|
|
|
writer.WriteLine(" break;"); |
|
|
|
|
writer.WriteLine("}"); |
|
|
|
|
foreach (FieldDescriptor field in sortedFields) { |
|
|
|
|
uint tag = WireFormat.MakeTag(field.FieldNumber, WireFormat.GetWireType(field.FieldType)); |
|
|
|
|
writer.WriteLine("case {0}: {{", tag); |
|
|
|
|
writer.Indent(); |
|
|
|
|
SourceGenerators.CreateFieldGenerator(field).GenerateParsingCode(writer); |
|
|
|
|
writer.WriteLine("break;"); |
|
|
|
|
writer.Outdent(); |
|
|
|
|
writer.WriteLine("}"); |
|
|
|
|
} |
|
|
|
|
writer.Outdent(); |
|
|
|
|
writer.WriteLine("}"); |
|
|
|
|
writer.Outdent(); |
|
|
|
|
writer.WriteLine("}"); |
|
|
|
|
writer.Outdent(); |
|
|
|
|
writer.WriteLine("}"); |
|
|
|
|
writer.WriteLine(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void GenerateIsInitialized(TextGenerator writer) { |
|
|
|
|
writer.WriteLine("public override bool IsInitialized {"); |
|
|
|
|
writer.Indent(); |
|
|
|
|
writer.WriteLine("get {"); |
|
|
|
|
writer.Indent(); |
|
|
|
|
|
|
|
|
|
// Check that all required fields in this message are set. |
|
|
|
|
// TODO(kenton): We can optimize this when we switch to putting all the |
|
|
|
|
// "has" fields into a single bitfield. |
|
|
|
|
foreach (FieldDescriptor field in Descriptor.Fields) { |
|
|
|
|
if (field.IsRequired) { |
|
|
|
|
writer.WriteLine("if (!has{0}) return false;", Helpers.UnderscoresToPascalCase(field.Name)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Now check that all embedded messages are initialized. |
|
|
|
|
foreach (FieldDescriptor field in Descriptor.Fields) { |
|
|
|
|
if (field.FieldType != FieldType.Message || |
|
|
|
|
!HasRequiredFields(field.MessageType, new Dictionary<MessageDescriptor, object>())) { |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
string propertyName = Helpers.UnderscoresToPascalCase(DescriptorUtil.GetFieldName(field)); |
|
|
|
|
if (field.IsRepeated) { |
|
|
|
|
writer.WriteLine("foreach ({0} element in {1}List) {{", DescriptorUtil.GetClassName(field.MessageType), propertyName); |
|
|
|
|
writer.WriteLine(" if (!element.IsInitialized) return false;"); |
|
|
|
|
writer.WriteLine("}"); |
|
|
|
|
} else if (field.IsOptional) { |
|
|
|
|
writer.WriteLine("if (Has{0}) {{", propertyName); |
|
|
|
|
writer.WriteLine(" if (!{0}.IsInitialized) return false;", propertyName); |
|
|
|
|
writer.WriteLine("}"); |
|
|
|
|
} else { |
|
|
|
|
writer.WriteLine("if (!{0}.IsInitialized) return false;", propertyName); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (Descriptor.Extensions.Count > 0) { |
|
|
|
|
writer.WriteLine("if (!ExtensionsAreInitialized) return false;"); |
|
|
|
|
} |
|
|
|
|
writer.WriteLine("return true;"); |
|
|
|
|
writer.Outdent(); |
|
|
|
|
writer.WriteLine("}"); |
|
|
|
|
writer.Outdent(); |
|
|
|
|
writer.WriteLine("}"); |
|
|
|
|
writer.WriteLine(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |