diff --git a/protos/google/protobuf/csharp_options.proto b/protos/google/protobuf/csharp_options.proto index 6d81c5cdec..29c7e8ad3b 100644 --- a/protos/google/protobuf/csharp_options.proto +++ b/protos/google/protobuf/csharp_options.proto @@ -10,16 +10,21 @@ message CSharpFileOptions { optional bool public_classes = 3; optional bool multiple_files = 4; optional bool nest_classes = 5; +} + +extend FileOptions { + optional CSharpFileOptions csharp_file_options = 1000; +} - extend FileOptions { - optional CSharpFileOptions csharp_options = 1000; - } +extend FieldOptions { + optional CSharpFieldOptions csharp_field_options = 1000; } message CSharpFieldOptions { + // Provides the ability to override the name of the property + // generated for this field. This does not currently work with + // messages optimised for reflection, and is only applied to the + // actual property rather than associated methods. (Careful + // consideration needed here...) optional string property_name = 1; - - extend FieldOptions { - optional CSharpFieldOptions csharp_options = 1000; - } } diff --git a/protos/google/protobuf/unittest.proto b/protos/google/protobuf/unittest.proto index 45ee42b134..443ba782d9 100644 --- a/protos/google/protobuf/unittest.proto +++ b/protos/google/protobuf/unittest.proto @@ -2,8 +2,8 @@ // line onwards is as per original distribution. import "google/protobuf/csharp_options.proto"; import "google/protobuf/descriptor.proto"; -option (google.protobuf.CSharpFileOptions.csharp_options).namespace = "Google.ProtocolBuffers.TestProtos"; -option (google.protobuf.CSharpFileOptions.csharp_options).umbrella_classname = "UnitTestProtoFile"; +option (google.protobuf.csharp_file_options).namespace = "Google.ProtocolBuffers.TestProtos"; +option (google.protobuf.csharp_file_options).umbrella_classname = "UnitTestProtoFile"; // Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. diff --git a/protos/google/protobuf/unittest_custom_options.proto b/protos/google/protobuf/unittest_custom_options.proto index 4452527990..49b578f419 100644 --- a/protos/google/protobuf/unittest_custom_options.proto +++ b/protos/google/protobuf/unittest_custom_options.proto @@ -1,8 +1,8 @@ // Additional options required for C# generation. File from copyright // line onwards is as per original distribution. import "google/protobuf/csharp_options.proto"; -option (google.protobuf.CSharpFileOptions.csharp_options).namespace = "Google.ProtocolBuffers.TestProtos"; -option (google.protobuf.CSharpFileOptions.csharp_options).umbrella_classname = "UnitTestCustomOptionsProtoFile"; +option (google.protobuf.csharp_file_options).namespace = "Google.ProtocolBuffers.TestProtos"; +option (google.protobuf.csharp_file_options).umbrella_classname = "UnitTestCustomOptionsProtoFile"; // Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. diff --git a/protos/google/protobuf/unittest_embed_optimize_for.proto b/protos/google/protobuf/unittest_embed_optimize_for.proto index 9ab1092a51..830df8fabe 100644 --- a/protos/google/protobuf/unittest_embed_optimize_for.proto +++ b/protos/google/protobuf/unittest_embed_optimize_for.proto @@ -2,8 +2,8 @@ // line onwards is as per original distribution. import "google/protobuf/csharp_options.proto"; import "google/protobuf/descriptor.proto"; -option (google.protobuf.CSharpFileOptions.csharp_options).namespace = "Google.ProtocolBuffers.TestProtos"; -option (google.protobuf.CSharpFileOptions.csharp_options).umbrella_classname = "UnitTestEmbedOptimizeForProtoFile"; +option (google.protobuf.csharp_file_options).namespace = "Google.ProtocolBuffers.TestProtos"; +option (google.protobuf.csharp_file_options).umbrella_classname = "UnitTestEmbedOptimizeForProtoFile"; // Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. diff --git a/protos/google/protobuf/unittest_import.proto b/protos/google/protobuf/unittest_import.proto index 7af5b74c02..bdabcc6932 100644 --- a/protos/google/protobuf/unittest_import.proto +++ b/protos/google/protobuf/unittest_import.proto @@ -2,8 +2,8 @@ // line onwards is as per original distribution. import "google/protobuf/csharp_options.proto"; import "google/protobuf/descriptor.proto"; -option (google.protobuf.CSharpFileOptions.csharp_options).namespace = "Google.ProtocolBuffers.TestProtos"; -option (google.protobuf.CSharpFileOptions.csharp_options).umbrella_classname = "UnitTestImportProtoFile"; +option (google.protobuf.csharp_file_options).namespace = "Google.ProtocolBuffers.TestProtos"; +option (google.protobuf.csharp_file_options).umbrella_classname = "UnitTestImportProtoFile"; // Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. diff --git a/protos/google/protobuf/unittest_mset.proto b/protos/google/protobuf/unittest_mset.proto index 74f30021a7..60d68eb9c1 100644 --- a/protos/google/protobuf/unittest_mset.proto +++ b/protos/google/protobuf/unittest_mset.proto @@ -2,8 +2,8 @@ // line onwards is as per original distribution. import "google/protobuf/csharp_options.proto"; import "google/protobuf/descriptor.proto"; -option (google.protobuf.CSharpFileOptions.csharp_options).namespace = "Google.ProtocolBuffers.TestProtos"; -option (google.protobuf.CSharpFileOptions.csharp_options).umbrella_classname = "UnitTestMessageSetProtoFile"; +option (google.protobuf.csharp_file_options).namespace = "Google.ProtocolBuffers.TestProtos"; +option (google.protobuf.csharp_file_options).umbrella_classname = "UnitTestMessageSetProtoFile"; // Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. diff --git a/protos/google/protobuf/unittest_optimize_for.proto b/protos/google/protobuf/unittest_optimize_for.proto index 204d09f96c..8f66c5e068 100644 --- a/protos/google/protobuf/unittest_optimize_for.proto +++ b/protos/google/protobuf/unittest_optimize_for.proto @@ -2,8 +2,8 @@ // line onwards is as per original distribution. import "google/protobuf/csharp_options.proto"; import "google/protobuf/descriptor.proto"; -option (google.protobuf.CSharpFileOptions.csharp_options).namespace = "Google.ProtocolBuffers.TestProtos"; -option (google.protobuf.CSharpFileOptions.csharp_options).umbrella_classname = "UnitTestOptimizeForProtoFile"; +option (google.protobuf.csharp_file_options).namespace = "Google.ProtocolBuffers.TestProtos"; +option (google.protobuf.csharp_file_options).umbrella_classname = "UnitTestOptimizeForProtoFile"; // Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. diff --git a/src/ProtoGen.Test/ProtoGen.Test.csproj b/src/ProtoGen.Test/ProtoGen.Test.csproj index a13344932b..426431b288 100644 --- a/src/ProtoGen.Test/ProtoGen.Test.csproj +++ b/src/ProtoGen.Test/ProtoGen.Test.csproj @@ -45,7 +45,6 @@ - diff --git a/src/ProtoGen/ExtensionGenerator.cs b/src/ProtoGen/ExtensionGenerator.cs index d1acacdf4b..2f6876c317 100644 --- a/src/ProtoGen/ExtensionGenerator.cs +++ b/src/ProtoGen/ExtensionGenerator.cs @@ -9,7 +9,7 @@ namespace Google.ProtocolBuffers.ProtoGen { } public void Generate(TextGenerator writer) { - string name = NameHelpers.UnderscoresToPascalCase(GetFieldName(Descriptor)); + string name = Descriptor.CSharpOptions.PropertyName; string type; switch (Descriptor.MappedType) { diff --git a/src/ProtoGen/FieldGeneratorBase.cs b/src/ProtoGen/FieldGeneratorBase.cs index a6e086ed3f..d20745a4ca 100644 --- a/src/ProtoGen/FieldGeneratorBase.cs +++ b/src/ProtoGen/FieldGeneratorBase.cs @@ -73,17 +73,9 @@ namespace Google.ProtocolBuffers.ProtoGen { } } - /// - /// Usually the same as CapitalizedName, except when the enclosing type has the same name, - /// in which case an underscore is appended. - /// protected string PropertyName { get { - string ret = CapitalizedName; - if (ret == Descriptor.ContainingType.Name) { - ret += "_"; - } - return ret; + return Descriptor.CSharpOptions.PropertyName; } } diff --git a/src/ProtoGen/Generator.cs b/src/ProtoGen/Generator.cs index f7a82cca5e..828b610c60 100644 --- a/src/ProtoGen/Generator.cs +++ b/src/ProtoGen/Generator.cs @@ -30,8 +30,8 @@ namespace Google.ProtocolBuffers.ProtoGen { foreach (string inputFile in options.InputFiles) { FileDescriptorSet descriptorProtos; ExtensionRegistry extensionRegistry = ExtensionRegistry.CreateInstance(); - extensionRegistry.Add(CSharpFileOptions.CSharpOptions); - extensionRegistry.Add(CSharpFieldOptions.CSharpOptions); + extensionRegistry.Add(CSharpOptions.CSharpFileOptions); + extensionRegistry.Add(CSharpOptions.CSharpFieldOptions); using (Stream inputStream = File.OpenRead(inputFile)) { descriptorProtos = FileDescriptorSet.ParseFrom(inputStream, extensionRegistry); } diff --git a/src/ProtoGen.Test/DescriptorUtilTest.cs b/src/ProtocolBuffers.Test/CSharpOptionsTest.cs similarity index 69% rename from src/ProtoGen.Test/DescriptorUtilTest.cs rename to src/ProtocolBuffers.Test/CSharpOptionsTest.cs index 2eb60ef129..d22f99de36 100644 --- a/src/ProtoGen.Test/DescriptorUtilTest.cs +++ b/src/ProtocolBuffers.Test/CSharpOptionsTest.cs @@ -1,69 +1,70 @@ -using Google.ProtocolBuffers.DescriptorProtos; +using Google.ProtocolBuffers.DescriptorProtos; using Google.ProtocolBuffers.Descriptors; using NUnit.Framework; -namespace Google.ProtocolBuffers.ProtoGen { +namespace Google.ProtocolBuffers { [TestFixture] public class DescriptorUtilTest { - /* FIXME: Move these around! [Test] public void ExplicitNamespace() { FileDescriptorProto proto = new FileDescriptorProto.Builder { - Name = "x", Package = "pack", Options = new FileOptions.Builder().SetExtension(CSharpOptions.CSharpNamespace, "Foo.Bar").Build() + Name = "x", Package = "pack", Options = new FileOptions.Builder().SetExtension(CSharpOptions.CSharpFileOptions, + new CSharpFileOptions.Builder { Namespace = "Foo.Bar" }.Build()).Build() }.Build(); FileDescriptor descriptor = FileDescriptor.BuildFrom(proto, null); - Assert.AreEqual("Foo.Bar", DescriptorUtil.GetNamespace(descriptor)); + Assert.AreEqual("Foo.Bar", descriptor.CSharpOptions.Namespace); } [Test] public void NoNamespaceFallsBackToPackage() { FileDescriptorProto proto = new FileDescriptorProto.Builder { Name = "x", Package = "pack" }.Build(); FileDescriptor descriptor = FileDescriptor.BuildFrom(proto, null); - Assert.AreEqual("pack", DescriptorUtil.GetNamespace(descriptor)); + Assert.AreEqual("pack", descriptor.CSharpOptions.Namespace); } [Test] public void NoNamespaceOrPackageFallsBackToEmptyString() { FileDescriptorProto proto = new FileDescriptorProto.Builder { Name = "x" }.Build(); FileDescriptor descriptor = FileDescriptor.BuildFrom(proto, null); - Assert.AreEqual("", DescriptorUtil.GetNamespace(descriptor)); + Assert.AreEqual("", descriptor.CSharpOptions.Namespace); } [Test] public void ExplicitlyNamedFileClass() { FileDescriptorProto proto = new FileDescriptorProto.Builder { - Name = "x", Options = new FileOptions.Builder().SetExtension(CSharpOptions.CSharpUmbrellaClassname, "Foo").Build() + Name = "x", Options = new FileOptions.Builder().SetExtension(CSharpOptions.CSharpFileOptions, + new CSharpFileOptions.Builder { UmbrellaClassname = "Foo" }.Build()).Build() }.Build(); FileDescriptor descriptor = FileDescriptor.BuildFrom(proto, null); - Assert.AreEqual("Foo", DescriptorUtil.GetUmbrellaClassName(descriptor)); + Assert.AreEqual("Foo", descriptor.CSharpOptions.UmbrellaClassname); } [Test] public void ImplicitFileClassWithProtoSuffix() { FileDescriptorProto proto = new FileDescriptorProto.Builder { Name = "foo_bar.proto" }.Build(); FileDescriptor descriptor = FileDescriptor.BuildFrom(proto, null); - Assert.AreEqual("FooBar", DescriptorUtil.GetUmbrellaClassName(descriptor)); + Assert.AreEqual("FooBar", descriptor.CSharpOptions.UmbrellaClassname); } [Test] public void ImplicitFileClassWithProtoDevelSuffix() { FileDescriptorProto proto = new FileDescriptorProto.Builder { Name = "foo_bar.protodevel" }.Build(); FileDescriptor descriptor = FileDescriptor.BuildFrom(proto, null); - Assert.AreEqual("FooBar", DescriptorUtil.GetUmbrellaClassName(descriptor)); + Assert.AreEqual("FooBar", descriptor.CSharpOptions.UmbrellaClassname); } [Test] public void ImplicitFileClassWithNoSuffix() { FileDescriptorProto proto = new FileDescriptorProto.Builder { Name = "foo_bar" }.Build(); FileDescriptor descriptor = FileDescriptor.BuildFrom(proto, null); - Assert.AreEqual("FooBar", DescriptorUtil.GetUmbrellaClassName(descriptor)); + Assert.AreEqual("FooBar", descriptor.CSharpOptions.UmbrellaClassname); } [Test] public void ImplicitFileClassWithDirectoryStructure() { FileDescriptorProto proto = new FileDescriptorProto.Builder { Name = "x/y/foo_bar" }.Build(); FileDescriptor descriptor = FileDescriptor.BuildFrom(proto, null); - Assert.AreEqual("FooBar", DescriptorUtil.GetUmbrellaClassName(descriptor)); - } */ + Assert.AreEqual("FooBar", descriptor.CSharpOptions.UmbrellaClassname); + } } } diff --git a/src/ProtocolBuffers.Test/ProtocolBuffers.Test.csproj b/src/ProtocolBuffers.Test/ProtocolBuffers.Test.csproj index 836ee2d8b7..277348cc55 100644 --- a/src/ProtocolBuffers.Test/ProtocolBuffers.Test.csproj +++ b/src/ProtocolBuffers.Test/ProtocolBuffers.Test.csproj @@ -51,6 +51,7 @@ + diff --git a/src/ProtocolBuffers.Test/TestProtos/___7469.tmp b/src/ProtocolBuffers.Test/TestProtos/___7469.tmp deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/ProtocolBuffers/DescriptorProtos/CSharpOptions.cs b/src/ProtocolBuffers/DescriptorProtos/CSharpOptions.cs index 55a30030ad..743a0c36dd 100644 --- a/src/ProtocolBuffers/DescriptorProtos/CSharpOptions.cs +++ b/src/ProtocolBuffers/DescriptorProtos/CSharpOptions.cs @@ -16,20 +16,27 @@ namespace Google.ProtocolBuffers.DescriptorProtos { global::System.Convert.FromBase64String( "CiRnb29nbGUvcHJvdG9idWYvY3NoYXJwX29wdGlvbnMucHJvdG8SD2dvb2ds" + "ZS5wcm90b2J1ZhogZ29vZ2xlL3Byb3RvYnVmL2Rlc2NyaXB0b3IucHJvdG8i" + - "4wEKEUNTaGFycEZpbGVPcHRpb25zEhEKCW5hbWVzcGFjZRgBIAEoCRIaChJ1" + + "iAEKEUNTaGFycEZpbGVPcHRpb25zEhEKCW5hbWVzcGFjZRgBIAEoCRIaChJ1" + "bWJyZWxsYV9jbGFzc25hbWUYAiABKAkSFgoOcHVibGljX2NsYXNzZXMYAyAB" + "KAgSFgoObXVsdGlwbGVfZmlsZXMYBCABKAgSFAoMbmVzdF9jbGFzc2VzGAUg" + - "ASgIMlkKDmNzaGFycF9vcHRpb25zEhwuZ29vZ2xlLnByb3RvYnVmLkZpbGVP" + - "cHRpb25zGOgHIAEoCzIiLmdvb2dsZS5wcm90b2J1Zi5DU2hhcnBGaWxlT3B0" + - "aW9ucyKIAQoSQ1NoYXJwRmllbGRPcHRpb25zEhUKDXByb3BlcnR5X25hbWUY" + - "ASABKAkyWwoOY3NoYXJwX29wdGlvbnMSHS5nb29nbGUucHJvdG9idWYuRmll" + - "bGRPcHRpb25zGOgHIAEoCzIjLmdvb2dsZS5wcm90b2J1Zi5DU2hhcnBGaWVs" + - "ZE9wdGlvbnM="), + "ASgIIisKEkNTaGFycEZpZWxkT3B0aW9ucxIVCg1wcm9wZXJ0eV9uYW1lGAEg" + + "ASgJOl4KE2NzaGFycF9maWxlX29wdGlvbnMSHC5nb29nbGUucHJvdG9idWYu" + + "RmlsZU9wdGlvbnMY6AcgASgLMiIuZ29vZ2xlLnByb3RvYnVmLkNTaGFycEZp" + + "bGVPcHRpb25zOmEKFGNzaGFycF9maWVsZF9vcHRpb25zEh0uZ29vZ2xlLnBy" + + "b3RvYnVmLkZpZWxkT3B0aW9ucxjoByABKAsyIy5nb29nbGUucHJvdG9idWYu" + + "Q1NoYXJwRmllbGRPcHRpb25z"), new pbd::FileDescriptor[] { global::Google.ProtocolBuffers.DescriptorProtos.DescriptorProtoFile.Descriptor, }); #endregion + #region Extensions + public static readonly pb::GeneratedExtensionBase CSharpFileOptions = + pb::GeneratedSingleExtension.CreateInstance(Descriptor.Extensions[0]); + public static readonly pb::GeneratedExtensionBase CSharpFieldOptions = + pb::GeneratedSingleExtension.CreateInstance(Descriptor.Extensions[1]); + #endregion + #region Static variables internal static readonly pbd::MessageDescriptor internal__static_google_protobuf_CSharpFileOptions__Descriptor = Descriptor.MessageTypes[0]; @@ -66,8 +73,6 @@ namespace Google.ProtocolBuffers.DescriptorProtos { get { return global::Google.ProtocolBuffers.DescriptorProtos.CSharpOptions.internal__static_google_protobuf_CSharpFileOptions__FieldAccessorTable; } } - public static readonly pb::GeneratedExtensionBase CSharpOptions = - pb::GeneratedSingleExtension.CreateInstance(Descriptor.Extensions[0]); private bool hasNamespace; private string namespace_ = ""; public bool HasNamespace { @@ -293,8 +298,6 @@ namespace Google.ProtocolBuffers.DescriptorProtos { get { return global::Google.ProtocolBuffers.DescriptorProtos.CSharpOptions.internal__static_google_protobuf_CSharpFieldOptions__FieldAccessorTable; } } - public static readonly pb::GeneratedExtensionBase CSharpOptions = - pb::GeneratedSingleExtension.CreateInstance(Descriptor.Extensions[0]); private bool hasPropertyName; private string propertyName_ = ""; public bool HasPropertyName { diff --git a/src/ProtocolBuffers/Descriptors/FieldDescriptor.cs b/src/ProtocolBuffers/Descriptors/FieldDescriptor.cs index 5ff5519732..0d0fad76c5 100644 --- a/src/ProtocolBuffers/Descriptors/FieldDescriptor.cs +++ b/src/ProtocolBuffers/Descriptors/FieldDescriptor.cs @@ -50,6 +50,9 @@ namespace Google.ProtocolBuffers.Descriptors { private FieldType fieldType; private MappedType mappedType; + private CSharpFieldOptions csharpFieldOptions; + private readonly object optionsLock = new object(); + internal FieldDescriptor(FieldDescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int index, bool isExtension) : base(proto, file, ComputeFullName(file, parent, proto.Name), index) { @@ -87,6 +90,31 @@ namespace Google.ProtocolBuffers.Descriptors { file.DescriptorPool.AddSymbol(this); } + private CSharpFieldOptions BuildOrFakeCSharpOptions() { + // TODO(jonskeet): Check if we could use FileDescriptorProto.Descriptor.Name - interesting bootstrap issues + if (File.Proto.Name == "google/protobuf/csharp_options.proto") { + if (Name=="csharp_field_options") { + return new CSharpFieldOptions.Builder { PropertyName = "CSharpFieldOptions" }.Build(); + } + if (Name=="csharp_file_options") { + return new CSharpFieldOptions.Builder { PropertyName = "CSharpFileOptions" }.Build(); + } + } + CSharpFieldOptions.Builder builder = CSharpFieldOptions.CreateBuilder(); + if (Proto.Options.HasExtension(DescriptorProtos.CSharpOptions.CSharpFieldOptions)) { + builder.MergeFrom(Proto.Options.GetExtension(DescriptorProtos.CSharpOptions.CSharpFieldOptions)); + } + if (!builder.HasPropertyName) { + string fieldName = FieldType == FieldType.Group ? MessageType.Name : Name; + string propertyName = NameHelpers.UnderscoresToPascalCase(fieldName); + if (propertyName == ContainingType.Name) { + propertyName += "_"; + } + builder.PropertyName = propertyName; + } + return builder.Build(); + } + /// /// Maps a field type as included in the .proto file to a FieldType. /// @@ -191,6 +219,21 @@ namespace Google.ProtocolBuffers.Descriptors { public MessageDescriptor ContainingType { get { return containingType; } } + + /// + /// Returns the C#-specific options for this file descriptor. This will always be + /// completely filled in. + /// + public CSharpFieldOptions CSharpOptions { + get { + lock (optionsLock) { + if (csharpFieldOptions == null) { + csharpFieldOptions = BuildOrFakeCSharpOptions(); + } + } + return csharpFieldOptions; + } + } /// /// For extensions defined nested within message types, gets diff --git a/src/ProtocolBuffers/Descriptors/FileDescriptor.cs b/src/ProtocolBuffers/Descriptors/FileDescriptor.cs index 79e034a1d0..81c4b790df 100644 --- a/src/ProtocolBuffers/Descriptors/FileDescriptor.cs +++ b/src/ProtocolBuffers/Descriptors/FileDescriptor.cs @@ -88,8 +88,8 @@ namespace Google.ProtocolBuffers.Descriptors { }.Build(); } CSharpFileOptions.Builder builder = CSharpFileOptions.CreateBuilder(); - if (proto.Options.HasExtension(CSharpFileOptions.CSharpOptions)) { - builder.MergeFrom(proto.Options.GetExtension(CSharpFileOptions.CSharpOptions)); + if (proto.Options.HasExtension(DescriptorProtos.CSharpOptions.CSharpFileOptions)) { + builder.MergeFrom(proto.Options.GetExtension(DescriptorProtos.CSharpOptions.CSharpFileOptions)); } if (!builder.HasNamespace) { builder.Namespace = Package; @@ -128,7 +128,6 @@ namespace Google.ProtocolBuffers.Descriptors { /// /// Returns the C#-specific options for this file descriptor. This will always be /// completely filled in. - /// FIXME: This isn't thread-safe. Can't do it at construction time due to bootstrapping issues. /// public CSharpFileOptions CSharpOptions { get {