Commit e60ce8bf authored by Jon Skeet's avatar Jon Skeet

Final commit before changing layout

parent 7f90d8ee
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using Google.ProtocolBuffers.Descriptors;
using NUnit.Framework; using NUnit.Framework;
using Google.ProtocolBuffers.DescriptorProtos;
using Google.ProtocolBuffers.ProtoGen;
namespace ProtoGen { namespace Google.ProtocolBuffers.ProtoGen {
/// <summary> /// <summary>
/// Tests for the dependency resolution in Generator. /// Tests for the dependency resolution in Generator.
/// </summary> /// </summary>
...@@ -12,6 +15,81 @@ namespace ProtoGen { ...@@ -12,6 +15,81 @@ namespace ProtoGen {
[Test] [Test]
public void TwoDistinctFiles() { public void TwoDistinctFiles() {
FileDescriptorProto first = new FileDescriptorProto.Builder { Name="First" }.Build();
FileDescriptorProto second = new FileDescriptorProto.Builder { Name="Second" }.Build();
FileDescriptorSet set = new FileDescriptorSet { FileList = { first, second } };
IList<FileDescriptor> converted = Generator.ConvertDescriptors(set);
Assert.AreEqual(2, converted.Count);
Assert.AreEqual("First", converted[0].Name);
Assert.AreEqual(0, converted[0].Dependencies.Count);
Assert.AreEqual("Second", converted[1].Name);
Assert.AreEqual(0, converted[1].Dependencies.Count);
}
[Test]
public void FirstDependsOnSecond() {
FileDescriptorProto first = new FileDescriptorProto.Builder { Name = "First", DependencyList = {"Second"} }.Build();
FileDescriptorProto second = new FileDescriptorProto.Builder { Name = "Second" }.Build();
FileDescriptorSet set = new FileDescriptorSet { FileList = { first, second } };
IList<FileDescriptor> converted = Generator.ConvertDescriptors(set);
Assert.AreEqual(2, converted.Count);
Assert.AreEqual("First", converted[0].Name);
Assert.AreEqual(1, converted[0].Dependencies.Count);
Assert.AreEqual(converted[1], converted[0].Dependencies[0]);
Assert.AreEqual("Second", converted[1].Name);
Assert.AreEqual(0, converted[1].Dependencies.Count);
}
[Test]
public void SecondDependsOnFirst() {
FileDescriptorProto first = new FileDescriptorProto.Builder { Name = "First" }.Build();
FileDescriptorProto second = new FileDescriptorProto.Builder { Name = "Second", DependencyList = {"First"} }.Build();
FileDescriptorSet set = new FileDescriptorSet { FileList = { first, second } };
IList<FileDescriptor> converted = Generator.ConvertDescriptors(set);
Assert.AreEqual(2, converted.Count);
Assert.AreEqual("First", converted[0].Name);
Assert.AreEqual(0, converted[0].Dependencies.Count);
Assert.AreEqual("Second", converted[1].Name);
Assert.AreEqual(1, converted[1].Dependencies.Count);
Assert.AreEqual(converted[0], converted[1].Dependencies[0]);
}
[Test]
public void CircularDependency() {
FileDescriptorProto first = new FileDescriptorProto.Builder { Name = "First", DependencyList = { "Second" } }.Build();
FileDescriptorProto second = new FileDescriptorProto.Builder { Name = "Second", DependencyList = { "First" } }.Build();
FileDescriptorSet set = new FileDescriptorSet { FileList = { first, second } };
try {
Generator.ConvertDescriptors(set);
Assert.Fail("Expected exception");
} catch (DependencyResolutionException) {
// Expected
}
}
[Test]
public void MissingDependency() {
FileDescriptorProto first = new FileDescriptorProto.Builder { Name = "First", DependencyList = { "Second" } }.Build();
FileDescriptorSet set = new FileDescriptorSet { FileList = { first } };
try {
Generator.ConvertDescriptors(set);
Assert.Fail("Expected exception");
} catch (DependencyResolutionException) {
// Expected
}
}
[Test]
public void SelfDependency() {
FileDescriptorProto first = new FileDescriptorProto.Builder { Name = "First", DependencyList = { "First" } }.Build();
FileDescriptorSet set = new FileDescriptorSet { FileList = { first } };
try {
Generator.ConvertDescriptors(set);
Assert.Fail("Expected exception");
} catch (DependencyResolutionException) {
// Expected
}
} }
} }
} }
\ No newline at end of file
using Google.ProtocolBuffers.DescriptorProtos;
using Google.ProtocolBuffers.Descriptors;
using NUnit.Framework;
namespace Google.ProtocolBuffers.ProtoGen {
[TestFixture]
public class DescriptorUtilTest {
[Test]
public void ExplicitNamespace() {
FileDescriptorProto proto = new FileDescriptorProto.Builder {
Name = "x", Package = "pack", Options = new FileOptions.Builder().SetExtension(CSharpOptions.CSharpNamespace, "Foo.Bar").Build()
}.Build();
FileDescriptor descriptor = FileDescriptor.BuildFrom(proto, null);
Assert.AreEqual("Foo.Bar", DescriptorUtil.GetNamespace(descriptor));
}
[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));
}
[Test]
public void NoNamespaceOrPackageFallsBackToEmptyString() {
FileDescriptorProto proto = new FileDescriptorProto.Builder { Name = "x" }.Build();
FileDescriptor descriptor = FileDescriptor.BuildFrom(proto, null);
Assert.AreEqual("", DescriptorUtil.GetNamespace(descriptor));
}
[Test]
public void ExplicitlyNamedFileClass() {
FileDescriptorProto proto = new FileDescriptorProto.Builder {
Name = "x", Options = new FileOptions.Builder().SetExtension(CSharpOptions.CSharpUmbrellaClassname, "Foo").Build()
}.Build();
FileDescriptor descriptor = FileDescriptor.BuildFrom(proto, null);
Assert.AreEqual("Foo", DescriptorUtil.GetUmbrellaClassName(descriptor));
}
[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));
}
[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));
}
[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));
}
[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));
}
}
}
using NUnit.Framework; using Google.ProtocolBuffers.DescriptorProtos;
using NUnit.Framework;
using Google.ProtocolBuffers.Descriptors;
namespace ProtoGen { namespace Google.ProtocolBuffers.ProtoGen {
[TestFixture] [TestFixture]
public class GeneratorTest { public class GeneratorTest {
} }
} }
\ No newline at end of file
using Google.ProtocolBuffers.ProtoGen;
using NUnit.Framework;
namespace Google.ProtocolBuffers.ProtoGen {
[TestFixture]
public class HelpersTest {
[Test]
public void UnderscoresToPascalCase() {
Assert.AreEqual("FooBar", Helpers.UnderscoresToPascalCase("Foo_bar"));
Assert.AreEqual("FooBar", Helpers.UnderscoresToPascalCase("foo_bar"));
Assert.AreEqual("Foo0Bar", Helpers.UnderscoresToPascalCase("Foo0bar"));
Assert.AreEqual("FooBar", Helpers.UnderscoresToPascalCase("Foo_+_Bar"));
}
[Test]
public void UnderscoresToCamelCase() {
Assert.AreEqual("fooBar", Helpers.UnderscoresToCamelCase("Foo_bar"));
Assert.AreEqual("fooBar", Helpers.UnderscoresToCamelCase("foo_bar"));
Assert.AreEqual("foo0Bar", Helpers.UnderscoresToCamelCase("Foo0bar"));
Assert.AreEqual("fooBar", Helpers.UnderscoresToCamelCase("Foo_+_Bar"));
}
[Test]
public void StripSuffix() {
string text = "FooBar";
Assert.IsFalse(Helpers.StripSuffix(ref text, "Foo"));
Assert.AreEqual("FooBar", text);
Assert.IsTrue(Helpers.StripSuffix(ref text, "Bar"));
Assert.AreEqual("Foo", text);
}
}
}
\ No newline at end of file
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
<ProjectGuid>{C268DA4C-4004-47DA-AF23-44C983281A68}</ProjectGuid> <ProjectGuid>{C268DA4C-4004-47DA-AF23-44C983281A68}</ProjectGuid>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder> <AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>ProtoGen</RootNamespace> <RootNamespace>Google.ProtocolBuffers.ProtoGen</RootNamespace>
<AssemblyName>Google.ProtocolBuffers.ProtoGen.Test</AssemblyName> <AssemblyName>Google.ProtocolBuffers.ProtoGen.Test</AssemblyName>
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion> <TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
...@@ -42,10 +42,14 @@ ...@@ -42,10 +42,14 @@
<HintPath>..\lib\Rhino.Mocks.dll</HintPath> <HintPath>..\lib\Rhino.Mocks.dll</HintPath>
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="DependencyResolutionTest.cs" /> <Compile Include="DependencyResolutionTest.cs" />
<Compile Include="DescriptorUtilTest.cs" />
<Compile Include="GeneratorTest.cs" /> <Compile Include="GeneratorTest.cs" />
<Compile Include="HelpersTest.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
......
using System;
using System.Collections.Generic;
using System.Text;
using Google.ProtocolBuffers.Descriptors;
using Google.ProtocolBuffers.DescriptorProtos;
namespace Google.ProtocolBuffers.ProtoGen {
/// <summary>
/// Utility class for determining namespaces etc.
/// </summary>
internal static class DescriptorUtil {
internal static bool NestClasses(IDescriptor descriptor) {
// Defaults to false
return descriptor.File.Options.GetExtension(CSharpOptions.CSharpNestClasses);
}
internal static string GetNamespace(FileDescriptor descriptor) {
if (descriptor.Name == "google/protobuf/descriptor.proto") {
return typeof(DescriptorProtoFile).Namespace;
}
return descriptor.Options.HasExtension(CSharpOptions.CSharpNamespace) ?
descriptor.Options.GetExtension(CSharpOptions.CSharpNamespace) : descriptor.Package;
}
// Groups are hacky: The name of the field is just the lower-cased name
// of the group type. In C#, though, we would like to retain the original
// capitalization of the type name.
internal static string GetFieldName(FieldDescriptor descriptor) {
if (descriptor.FieldType == FieldType.Group) {
return descriptor.MessageType.Name;
} else {
return descriptor.Name;
}
}
internal static string GetClassName(IDescriptor descriptor) {
return ToCSharpName(descriptor.FullName, descriptor.File);
}
internal static string GetFullUmbrellaClassName(FileDescriptor descriptor) {
string result = GetNamespace(descriptor);
if (result != "") result += '.';
result += GetUmbrellaClassName(descriptor);
return "global::" + result;
}
internal static string GetUmbrellaClassName(FileDescriptor descriptor) {
if (descriptor.Name == "google/protobuf/descriptor.proto") {
return typeof(DescriptorProtoFile).Name;
}
FileOptions options = descriptor.Options;
if (options.HasExtension(CSharpOptions.CSharpUmbrellaClassname)) {
return descriptor.Options.GetExtension(CSharpOptions.CSharpUmbrellaClassname);
}
int lastSlash = descriptor.Name.LastIndexOf('/');
string baseName = descriptor.Name.Substring(lastSlash + 1);
return Helpers.UnderscoresToPascalCase(StripProto(baseName));
}
private static string StripProto(string text) {
if (!Helpers.StripSuffix(ref text, ".protodevel")) {
Helpers.StripSuffix(ref text, ".proto");
}
return text;
}
private static string ToCSharpName(string name, FileDescriptor file) {
string result;
if (!NestClasses(file)) {
result = GetNamespace(file);
} else {
result = GetUmbrellaClassName(file);
}
if (result != "") {
result += '.';
}
string classname;
if (file.Package == "") {
classname = name;
} else {
// Strip the proto package from full_name since we've replaced it with
// the C# namespace.
classname = name.Substring(file.Package.Length + 1);
}
result += classname.Replace(".", ".Types.");
return "global::" + result;
}
internal static string GetMappedTypeName(MappedType type) {
switch(type) {
case MappedType.Int32: return "int";
case MappedType.Int64: return "long";
case MappedType.UInt32: return "uint";
case MappedType.UInt64: return "ulong";
case MappedType.Single: return "float";
case MappedType.Double: return "double";
case MappedType.Boolean: return "bool";
case MappedType.String: return "string";
case MappedType.ByteString: return "pb::ByteString";
case MappedType.Enum: return null;
case MappedType.Message: return null;
default:
throw new ArgumentOutOfRangeException("Unknown mapped type " + type);
}
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
using Google.ProtocolBuffers.Descriptors;
namespace Google.ProtocolBuffers.ProtoGen {
internal class EnumFieldGenerator : FieldGeneratorBase, IFieldSourceGenerator {
internal EnumFieldGenerator(FieldDescriptor descriptor)
: base(descriptor) {
}
public void GenerateMembers(TextGenerator writer) {
writer.WriteLine("private bool has{0};", CapitalizedName);
writer.WriteLine("private {0} {1}_ = {2};", TypeName, Name, DefaultValue);
writer.WriteLine("public bool Has{0} {{", CapitalizedName);
writer.WriteLine(" get {{ return has{0}; }}", CapitalizedName);
writer.WriteLine("}");
writer.WriteLine("public {0} {1} {{", TypeName, PropertyName);
writer.WriteLine(" get {{ return {0}_; }}", Name);
writer.WriteLine("}");
}
public void GenerateBuilderMembers(TextGenerator writer) {
writer.WriteLine("public bool Has{0} {{", CapitalizedName);
writer.WriteLine(" get {{ return result.Has{0}; }}", CapitalizedName);
writer.WriteLine("}");
writer.WriteLine("public {0} {1} {{", TypeName, PropertyName);
writer.WriteLine(" get {{ return result.{0}; }}", PropertyName);
writer.WriteLine(" set {{ Set{0}(value); }}", CapitalizedName);
writer.WriteLine("}");
writer.WriteLine("public Builder Set{0}({1} value) {{", CapitalizedName, TypeName);
writer.WriteLine(" result.has{0} = true;", CapitalizedName);
writer.WriteLine(" result.{0}_ = value;", Name);
writer.WriteLine(" return this;");
writer.WriteLine("}");
writer.WriteLine("public Builder Clear{0}() {{", CapitalizedName);
writer.WriteLine(" result.has{0} = false;", CapitalizedName);
writer.WriteLine(" result.{0}_ = {1};", Name, DefaultValue);
writer.WriteLine(" return this;");
writer.WriteLine("}");
}
public void GenerateMergingCode(TextGenerator writer) {
writer.WriteLine("if (other.Has{0}) {{", CapitalizedName);
writer.WriteLine(" {0} = other.{0};", PropertyName);
writer.WriteLine("}");
}
public void GenerateBuildingCode(TextGenerator writer) {
// Nothing to do here for enum types
}
public void GenerateParsingCode(TextGenerator writer) {
// TODO(jonskeet): Make a more efficient way of doing this
writer.WriteLine("int rawValue = input.ReadEnum();");
writer.WriteLine("if (!global::System.Enum.IsDefined(typeof({0}), rawValue)) {{", TypeName);
writer.WriteLine(" unknownFields.MergeVarintField({0}, (ulong) rawValue);", Number);
writer.WriteLine("} else {");
writer.WriteLine(" {0} = ({1}) rawValue;", PropertyName, TypeName);
writer.WriteLine("}");
}
public void GenerateSerializationCode(TextGenerator writer) {
writer.WriteLine("if (Has{0}) {{", CapitalizedName);
writer.WriteLine(" output.WriteEnum({0}, (int) {1});", Number, PropertyName);
writer.WriteLine("}");
}
public void GenerateSerializedSizeCode(TextGenerator writer) {
writer.WriteLine("if (Has{0}) {{", CapitalizedName);
writer.WriteLine(" size += pb::CodedOutputStream.ComputeEnumSize({0}, (int) {1});", Number, PropertyName);
writer.WriteLine("}");
}
}
}
using Google.ProtocolBuffers.Descriptors;
namespace Google.ProtocolBuffers.ProtoGen {
internal class EnumGenerator : SourceGeneratorBase<EnumDescriptor>, ISourceGenerator {
internal EnumGenerator(EnumDescriptor descriptor) : base(descriptor) {
}
public void Generate(TextGenerator writer) {
writer.WriteLine("{0} enum {1} {{", ClassAccessLevel, Descriptor.Name);
writer.Indent();
foreach (EnumValueDescriptor value in Descriptor.Values) {
writer.WriteLine("{0} = {1},", value.Name, value.Number);
}
writer.Outdent();
writer.WriteLine("}");
writer.WriteLine();
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
using Google.ProtocolBuffers.Descriptors;
namespace Google.ProtocolBuffers.ProtoGen {
internal class ExtensionGenerator : SourceGeneratorBase<FieldDescriptor>, ISourceGenerator {
internal ExtensionGenerator(FieldDescriptor descriptor) : base(descriptor) {
}
public void Generate(TextGenerator writer) {
string name = Helpers.UnderscoresToPascalCase(DescriptorUtil.GetFieldName(Descriptor));
string type;
switch (Descriptor.MappedType) {
case MappedType.Message:
type = DescriptorUtil.GetClassName(Descriptor.MessageType);
break;
case MappedType.Enum:
type = DescriptorUtil.GetClassName(Descriptor.EnumType);
break;
default:
type = DescriptorUtil.GetMappedTypeName(Descriptor.MappedType);
break;
}
if (Descriptor.IsRepeated) {
writer.WriteLine("{0} static readonly", ClassAccessLevel);
writer.WriteLine(" pb::GeneratedExtensionBase<scg::IList<{0}>> {1} =", type, name);
writer.WriteLine(" pb::GeneratedRepeatExtension<{0}>.CreateInstance(Descriptor.Extensions[{1}]);", type, Descriptor.Index);
} else {
writer.WriteLine("{0} static readonly pb::GeneratedExtensionBase<{1}> {2} =", ClassAccessLevel, type, name);
writer.WriteLine(" pb::GeneratedSingleExtension<{0}>.CreateInstance(Descriptor.Extensions[{1}]);", type, Descriptor.Index);
}
}
}
}
using System;
using Google.ProtocolBuffers.Descriptors;
using System.Globalization;
namespace Google.ProtocolBuffers.ProtoGen {
internal abstract class FieldGeneratorBase : SourceGeneratorBase<FieldDescriptor> {
protected FieldGeneratorBase(FieldDescriptor descriptor)
: base(descriptor) {
}
private static bool AllPrintableAscii(string text) {
foreach (char c in text) {
if (c < 0x20 || c > 0x7e) {
return false;
}
}
return true;
}
protected string DefaultValue {
get {
string suffix = "";
switch (Descriptor.FieldType) {
case FieldType.Float: suffix = "F"; break;
case FieldType.Double: suffix = "D"; break;
case FieldType.Int64: suffix = "L"; break;
case FieldType.UInt64: suffix = "UL"; break;
}
switch (Descriptor.FieldType) {
case FieldType.Float:
case FieldType.Double:
case FieldType.Int32:
case FieldType.Int64:
case FieldType.SInt32:
case FieldType.SInt64:
case FieldType.SFixed32:
case FieldType.SFixed64:
case FieldType.UInt32:
case FieldType.UInt64:
case FieldType.Fixed32:
case FieldType.Fixed64:
// The simple Object.ToString converts using the current culture.
// We want to always use the invariant culture so it's predictable.
IConvertible value = (IConvertible) Descriptor.DefaultValue;
return value.ToString(CultureInfo.InvariantCulture) + suffix;
case FieldType.Bool:
return (bool) Descriptor.DefaultValue ? "true" : "false";
case FieldType.Bytes:
if (!Descriptor.HasDefaultValue) {
return "pb::ByteString.Empty";
}
return string.Format("(pb::ByteString) {0}.Descriptor.Fields[{1}].DefaultValue", TypeName, Number);
case FieldType.String:
if (AllPrintableAscii(Descriptor.Proto.DefaultValue)) {
// All chars are ASCII and printable. In this case we only
// need to escape quotes and backslashes.
return "\"" + Descriptor.Proto.DefaultValue
.Replace("\\", "\\\\")
.Replace("'", "\\'")
.Replace("\"", "\\\"")
+ "\"";
}
return string.Format("(string) {0}.Descriptor.Fields[{1}].DefaultValue", TypeName, Number);
case FieldType.Enum:
return TypeName + "." + ((EnumValueDescriptor) Descriptor.DefaultValue).Name;
case FieldType.Message:
case FieldType.Group:
return TypeName + ".DefaultInstance";
default:
throw new InvalidOperationException("Invalid field descriptor type");
}
}
}
/// <summary>
/// Usually the same as CapitalizedName, except when the enclosing type has the same name,
/// in which case an underscore is appended.
/// </summary>
protected string PropertyName {
get {
string ret = CapitalizedName;
if (ret == Descriptor.ContainingType.Name) {
ret += "_";
}
return ret;
}
}
protected string CapitalizedName {
get { return Helpers.UnderscoresToPascalCase(DescriptorUtil.GetFieldName(Descriptor)); }
}
protected string Name {
get { return Helpers.UnderscoresToCamelCase(DescriptorUtil.GetFieldName(Descriptor)); }
}
protected int Number {
get { return Descriptor.FieldNumber; }
}
protected string TypeName {
get {
switch (Descriptor.FieldType) {
case FieldType.Enum:
return DescriptorUtil.GetClassName(Descriptor.EnumType);
case FieldType.Message:
case FieldType.Group:
return DescriptorUtil.GetClassName(Descriptor.MessageType);
default:
return DescriptorUtil.GetMappedTypeName(Descriptor.MappedType);
}
}
}
protected string MessageOrGroup {
get { return Descriptor.FieldType == FieldType.Group ? "Group" : "Message"; }
}
/// <summary>
/// Returns the type name as used in CodedInputStream method names: SFixed32, UInt32 etc.
/// </summary>
protected string CapitalizedTypeName {
get {
// Our enum names match perfectly. How serendipitous.
return Descriptor.FieldType.ToString();
}
}
}
}
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text;
using Google.ProtocolBuffers.DescriptorProtos; using Google.ProtocolBuffers.DescriptorProtos;
using System.IO; using System.IO;
using Google.ProtocolBuffers.Descriptors; using Google.ProtocolBuffers.Descriptors;
using Google.ProtocolBuffers.Collections;
namespace Google.ProtocolBuffers.ProtoGen { namespace Google.ProtocolBuffers.ProtoGen {
/// <summary> /// <summary>
...@@ -27,21 +29,110 @@ namespace Google.ProtocolBuffers.ProtoGen { ...@@ -27,21 +29,110 @@ namespace Google.ProtocolBuffers.ProtoGen {
public void Generate() { public void Generate() {
foreach (string inputFile in options.InputFiles) { foreach (string inputFile in options.InputFiles) {
FileDescriptorSet descriptorProtos; FileDescriptorSet descriptorProtos;
ExtensionRegistry extensionRegistry = ExtensionRegistry.CreateInstance();
extensionRegistry.Add(CSharpOptions.CSharpUmbrellaClassname);
extensionRegistry.Add(CSharpOptions.CSharpMultipleFiles);
extensionRegistry.Add(CSharpOptions.CSharpNamespace);
extensionRegistry.Add(CSharpOptions.CSharpNestClasses);
extensionRegistry.Add(CSharpOptions.CSharpPublicClasses);
using (Stream inputStream = File.OpenRead(inputFile)) { using (Stream inputStream = File.OpenRead(inputFile)) {
descriptorProtos = FileDescriptorSet.ParseFrom(inputStream); descriptorProtos = FileDescriptorSet.ParseFrom(inputStream, extensionRegistry);
}
IList<FileDescriptor> descriptors = ConvertDescriptors(descriptorProtos);
foreach (FileDescriptor descriptor in descriptors) {
Generate(descriptor);
} }
List<FileDescriptor> descriptors = ConvertDescriptors(descriptorProtos);
} }
} }
/// <summary>
/// Generates code for a particular file. All dependencies must
/// already have been resolved.
/// </summary>
private void Generate(FileDescriptor descriptor) {
string umbrellaClass = DescriptorUtil.GetUmbrellaClassName(descriptor);
string ns = DescriptorUtil.GetNamespace(descriptor);
using (TextWriter textWriter = File.CreateText(Path.Combine(options.OutputDirectory, umbrellaClass + ".cs"))) {
TextGenerator writer = new TextGenerator(textWriter);
UmbrellaClassGenerator ucg = new UmbrellaClassGenerator(descriptor);
ucg.Generate(writer);
/*
GenerateSiblings(umbrellaSource, descriptor, descriptor.MessageTypes);
GenerateSiblings(umbrellaSource, descriptor, descriptor.EnumTypes);
GenerateSiblings(umbrellaSource, descriptor, descriptor.Services);*/
}
}
private static void GenerateSiblings<T>(SourceFileGenerator parentSourceGenerator, FileDescriptor file, IEnumerable<T> siblings)
where T : IDescriptor {
}
/// <summary> /// <summary>
/// Resolves any dependencies and converts FileDescriptorProtos into FileDescriptors. /// Resolves any dependencies and converts FileDescriptorProtos into FileDescriptors.
/// The list returned is in the same order as the protos are listed in the descriptor set. /// The list returned is in the same order as the protos are listed in the descriptor set.
/// Note: this method is internal rather than private to allow testing. /// Note: this method is internal rather than private to allow testing.
/// </summary> /// </summary>
/// <exception cref="DependencyResolutionException">Not all dependencies could be resolved.</exception> /// <exception cref="DependencyResolutionException">Not all dependencies could be resolved.</exception>
internal static List<FileDescriptor> ConvertDescriptors(FileDescriptorSet descriptorProtos) { internal static IList<FileDescriptor> ConvertDescriptors(FileDescriptorSet descriptorProtos) {
return null; // Simple strategy: Keep going through the list of protos to convert, only doing ones where
// we've already converted all the dependencies, until we get to a stalemate
IList<FileDescriptorProto> fileList = descriptorProtos.FileList;
FileDescriptor[] converted = new FileDescriptor[fileList.Count];
Dictionary<string, FileDescriptor> convertedMap = new Dictionary<string, FileDescriptor>();
int totalConverted = 0;
bool madeProgress = true;
while (madeProgress && totalConverted < converted.Length) {
madeProgress = false;
for (int i = 0; i < converted.Length; i++) {
if (converted[i] != null) {
// Already done this one
continue;
}
FileDescriptorProto candidate = fileList[i];
FileDescriptor[] dependencies = new FileDescriptor[candidate.DependencyList.Count];
bool foundAllDependencies = true;
for (int j = 0; j < dependencies.Length; j++) {
if (!convertedMap.TryGetValue(candidate.DependencyList[j], out dependencies[j])) {
foundAllDependencies = false;
break;
}
}
if (!foundAllDependencies) {
continue;
}
madeProgress = true;
totalConverted++;
converted[i] = FileDescriptor.BuildFrom(candidate, dependencies);
convertedMap[candidate.Name] = converted[i];
}
}
if (!madeProgress) {
StringBuilder remaining = new StringBuilder();
for (int i = 0; i < converted.Length; i++) {
if (converted[i] == null) {
if (remaining.Length != 0) {
remaining.Append(", ");
}
FileDescriptorProto failure = fileList[i];
remaining.Append(failure.Name);
remaining.Append(":");
foreach (string dependency in failure.DependencyList) {
if (!convertedMap.ContainsKey(dependency)) {
remaining.Append(" ");
remaining.Append(dependency);
}
}
remaining.Append(";");
}
}
throw new DependencyResolutionException("Unable to resolve all dependencies: " + remaining);
}
return Lists.AsReadOnly(converted);
} }
} }
} }
using System;
using System.Text;
using Google.ProtocolBuffers.DescriptorProtos;
using Google.ProtocolBuffers.Descriptors;
namespace Google.ProtocolBuffers.ProtoGen {
/// <summary>
/// Helpers to resolve class names etc.
/// </summary>
internal static class Helpers {
internal static string UnderscoresToPascalCase(string input) {
return UnderscoresToPascalOrCamelCase(input, true);
}
internal static string UnderscoresToCamelCase(string input) {
return UnderscoresToPascalOrCamelCase(input, false);
}
internal static void WriteNamespaces(TextGenerator writer) {
writer.WriteLine("using pb = global::Google.ProtocolBuffers;");
writer.WriteLine("using pbc = global::Google.ProtocolBuffers.Collections;");
writer.WriteLine("using pbd = global::Google.ProtocolBuffers.Descriptors;");
writer.WriteLine("using scg = global::System.Collections.Generic;");
}
/// <summary>
/// Converts a string to Pascal or Camel case. The first letter is capitalized or
/// lower-cased depending on <paramref name="pascal"/> is true.
/// After the first letter, any punctuation is removed but triggers capitalization
/// of the next letter. Digits are preserved but trigger capitalization of the next
/// letter.
/// All capitalisation is done in the invariant culture.
/// </summary>
private static string UnderscoresToPascalOrCamelCase(string input, bool pascal) {
StringBuilder result = new StringBuilder();
bool capitaliseNext = pascal;
for (int i=0; i < input.Length; i++) {
char c = input[i];
if ('a' <= c && c <= 'z') {
if (capitaliseNext) {
result.Append(char.ToUpperInvariant(c));
} else {
result.Append(c);
}
capitaliseNext = false;
} else if ('A' <= c && c <= 'Z') {
if (i == 0 && !pascal) {
// Force first letter to lower-case unless explicitly told to
// capitalize it.
result.Append(char.ToLowerInvariant(c));
} else {
// Capital letters after the first are left as-is.
result.Append(c);
}
capitaliseNext = false;
} else if ('0' <= c && c <= '9') {
result.Append(c);
capitaliseNext = true;
} else {
capitaliseNext = true;
}
}
return result.ToString();
}
/// <summary>
/// Attempts to strip a suffix from a string, returning whether
/// or not the suffix was actually present.
/// </summary>
internal static bool StripSuffix(ref string text, string suffix) {
if (text.EndsWith(suffix)) {
text = text.Substring(0, text.Length - suffix.Length);
return true;
}
return false;
}
}
}
namespace Google.ProtocolBuffers.ProtoGen {
internal interface IFieldSourceGenerator {
void GenerateMembers(TextGenerator writer);
void GenerateBuilderMembers(TextGenerator writer);
void GenerateMergingCode(TextGenerator writer);
void GenerateBuildingCode(TextGenerator writer);
void GenerateParsingCode(TextGenerator writer);
void GenerateSerializationCode(TextGenerator writer);
void GenerateSerializedSizeCode(TextGenerator writer);
}
}
namespace Google.ProtocolBuffers.ProtoGen {
internal interface ISourceGenerator {
void Generate(TextGenerator writer);
}
}
using Google.ProtocolBuffers.Descriptors;
namespace Google.ProtocolBuffers.ProtoGen {
internal class MessageFieldGenerator : FieldGeneratorBase, IFieldSourceGenerator {
internal MessageFieldGenerator(FieldDescriptor descriptor)
: base(descriptor) {
}
public void GenerateMembers(TextGenerator writer) {
writer.WriteLine("private bool has{0};", CapitalizedName);
writer.WriteLine("private {0} {1}_ = {2};", TypeName, Name, DefaultValue);
writer.WriteLine("public bool Has{0} {{", CapitalizedName);
writer.WriteLine(" get {{ return has{0}; }}", CapitalizedName);
writer.WriteLine("}");
writer.WriteLine("public {0} {1} {{", TypeName, CapitalizedName);
writer.WriteLine(" get {{ return {0}_; }}", Name);
writer.WriteLine("}");
}
public void GenerateBuilderMembers(TextGenerator writer) {
writer.WriteLine("public bool Has{0} {{", CapitalizedName);
writer.WriteLine(" get {{ return result.Has{0}; }}", CapitalizedName);
writer.WriteLine("}");
writer.WriteLine("public {0} {1} {{", TypeName, CapitalizedName);
writer.WriteLine(" get {{ return result.{0}; }}", CapitalizedName);
writer.WriteLine(" set {{ Set{0}(value); }}", CapitalizedName);
writer.WriteLine("}");
writer.WriteLine("public Builder Set{0}({1} value) {{", CapitalizedName, TypeName);
writer.WriteLine(" result.has{0} = true;", CapitalizedName);
writer.WriteLine(" result.{0}_ = value;", Name);
writer.WriteLine(" return this;");
writer.WriteLine("}");
writer.WriteLine("public Builder Set{0}({1}.Builder builderForValue) {{", CapitalizedName, TypeName);
writer.WriteLine(" result.has{0} = true;", CapitalizedName);
writer.WriteLine(" result.{0}_ = builderForValue.Build();", Name);
writer.WriteLine(" return this;");
writer.WriteLine("}");
writer.WriteLine("public Builder Merge{0}({1} value) {{", CapitalizedName, TypeName);
writer.WriteLine(" if (result.Has{0} &&", CapitalizedName);
writer.WriteLine(" result.{0}_ != {1}) {{", Name, DefaultValue);
writer.WriteLine(" result.{0}_ = {1}.CreateBuilder(result.{0}_).MergeFrom(value).BuildPartial();", Name, TypeName);
writer.WriteLine(" } else {");
writer.WriteLine(" result.{0}_ = value;", Name);
writer.WriteLine(" }");
writer.WriteLine(" result.has{0} = true;", CapitalizedName);
writer.WriteLine(" return this;");
writer.WriteLine("}");
writer.WriteLine("public Builder Clear{0}() {{", CapitalizedName);
writer.WriteLine(" result.has{0} = false;", CapitalizedName);
writer.WriteLine(" result.{0}_ = {1};", Name, DefaultValue);
writer.WriteLine(" return this;");
writer.WriteLine("}");
}
public void GenerateMergingCode(TextGenerator writer) {
writer.WriteLine("if (other.Has{0}) {{", CapitalizedName);
writer.WriteLine(" Merge{0}(other.{0});", CapitalizedName);
writer.WriteLine("}");
}
public void GenerateBuildingCode(TextGenerator writer) {
// Nothing to do for singular fields
}
public void GenerateParsingCode(TextGenerator writer) {
writer.WriteLine("{0}.Builder subBuilder = {0}.CreateBuilder();", TypeName);
writer.WriteLine("if (Has{0}) {{", CapitalizedName);
writer.WriteLine(" subBuilder.MergeFrom({0});", CapitalizedName);
writer.WriteLine("}");
if (Descriptor.FieldType == FieldType.Group) {
writer.WriteLine("input.ReadGroup({0}, subBuilder, extensionRegistry);", Number);
} else {
writer.WriteLine("input.ReadMessage(subBuilder, extensionRegistry);");
}
writer.WriteLine("{0} = subBuilder.BuildPartial();", CapitalizedName);
}
public void GenerateSerializationCode(TextGenerator writer) {
writer.WriteLine("if (Has{0}) {{", CapitalizedName);
writer.WriteLine(" output.Write{0}({1}, {2});", MessageOrGroup, Number, CapitalizedName);
writer.WriteLine("}");
}
public void GenerateSerializedSizeCode(TextGenerator writer) {
writer.WriteLine("if (Has{0}) {{", CapitalizedName);
writer.WriteLine(" size += pb::CodedOutputStream.Compute{0}Size({1}, {2});",
MessageOrGroup, Number, CapitalizedName);
writer.WriteLine("}");
}
}
}
This diff is collapsed.
using System;
using Google.ProtocolBuffers.Descriptors;
namespace Google.ProtocolBuffers.ProtoGen {
// TODO(jonskeet): Refactor this. There's loads of common code here.
internal class PrimitiveFieldGenerator : FieldGeneratorBase, IFieldSourceGenerator {
internal PrimitiveFieldGenerator(FieldDescriptor descriptor)
: base(descriptor) {
}
public void GenerateMembers(TextGenerator writer) {
writer.WriteLine("private bool has{0};", CapitalizedName);
writer.WriteLine("private {0} {1}_ = {2};", TypeName, Name, DefaultValue);
writer.WriteLine("public bool Has{0} {{", CapitalizedName);
writer.WriteLine(" get {{ return has{0}; }}", CapitalizedName);
writer.WriteLine("}");
writer.WriteLine("public {0} {1} {{", TypeName, PropertyName);
writer.WriteLine(" get {{ return {0}_; }}", Name);
writer.WriteLine("}");
}
public void GenerateBuilderMembers(TextGenerator writer) {
writer.WriteLine("public bool Has{0} {{", CapitalizedName);
writer.WriteLine(" get {{ return result.Has{0}; }}", CapitalizedName);
writer.WriteLine("}");
writer.WriteLine("public {0} {1} {{", TypeName, PropertyName);
writer.WriteLine(" get {{ return result.{0}; }}", PropertyName);
writer.WriteLine(" set {{ Set{0}(value); }}", CapitalizedName);
writer.WriteLine("}");
writer.WriteLine("public Builder Set{0}({1} value) {{", CapitalizedName, TypeName);
writer.WriteLine(" result.has{0} = true;", CapitalizedName);
writer.WriteLine(" result.{0}_ = value;", Name);
writer.WriteLine(" return this;");
writer.WriteLine("}");
writer.WriteLine("public Builder Clear{0}() {{", CapitalizedName);
writer.WriteLine(" result.has{0} = false;", CapitalizedName);
writer.WriteLine(" result.{0}_ = {1};", Name, DefaultValue);
writer.WriteLine(" return this;");
writer.WriteLine("}");
}
public void GenerateMergingCode(TextGenerator writer) {
writer.WriteLine("if (other.Has{0}) {{", CapitalizedName);
writer.WriteLine(" {0} = other.{0};", PropertyName);
writer.WriteLine("}");
}
public void GenerateBuildingCode(TextGenerator writer) {
// Nothing to do here for primitive types
}
public void GenerateParsingCode(TextGenerator writer) {
writer.WriteLine("{0} = input.Read{1}();", PropertyName, CapitalizedTypeName);
}
public void GenerateSerializationCode(TextGenerator writer) {
writer.WriteLine("if (Has{0}) {{", CapitalizedName);
writer.WriteLine(" output.Write{0}({1}, {2});", CapitalizedTypeName, Number, PropertyName);
writer.WriteLine("}");
}
public void GenerateSerializedSizeCode(TextGenerator writer) {
writer.WriteLine("if (Has{0}) {{", CapitalizedName);
writer.WriteLine(" size += pb::CodedOutputStream.Compute{0}Size({1}, {2});",
CapitalizedTypeName, Number, PropertyName);
writer.WriteLine("}");
}
}
}
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Google.ProtocolBuffers.DescriptorProtos;
namespace Google.ProtocolBuffers.ProtoGen { namespace Google.ProtocolBuffers.ProtoGen {
/// <summary> /// <summary>
...@@ -8,7 +9,8 @@ namespace Google.ProtocolBuffers.ProtoGen { ...@@ -8,7 +9,8 @@ namespace Google.ProtocolBuffers.ProtoGen {
class Program { class Program {
static int Main(string[] args) { static int Main(string[] args) {
try { try {
// Hack to make sure everything's initialized
DescriptorProtoFile.Descriptor.ToString();
GeneratorOptions options = ParseCommandLineArguments(args); GeneratorOptions options = ParseCommandLineArguments(args);
IList<string> validationFailures; IList<string> validationFailures;
...@@ -25,16 +27,20 @@ namespace Google.ProtocolBuffers.ProtoGen { ...@@ -25,16 +27,20 @@ namespace Google.ProtocolBuffers.ProtoGen {
return 0; return 0;
} catch (Exception e) { } catch (Exception e) {
Console.Error.WriteLine("Caught unhandled exception: {0}", e); Console.Error.WriteLine("Error: {0}", e.Message);
Console.Error.WriteLine();
Console.Error.WriteLine("Detailed exception information: {0}", e);
return 1; return 1;
} }
} }
private static GeneratorOptions ParseCommandLineArguments(string[] args) { private static GeneratorOptions ParseCommandLineArguments(string[] args) {
GeneratorOptions options = new GeneratorOptions(); GeneratorOptions options = new GeneratorOptions();
string baseDir = "c:\\Users\\Jon\\Documents\\Visual Studio 2008\\Projects\\ProtocolBuffers"; //string baseDir = "c:\\Users\\Jon\\Documents\\Visual Studio 2008\\Projects\\ProtocolBuffers";
options.OutputDirectory = baseDir + "\\tmp"; //options.OutputDirectory = baseDir + "\\tmp";
options.InputFiles = new[] { baseDir + "\\protos\\nwind.protobin" }; //options.InputFiles = new[] { baseDir + "\\protos\\nwind-solo.protobin" };
options.OutputDirectory = ".";
options.InputFiles = args;
return options; return options;
} }
} }
......
...@@ -38,12 +38,31 @@ ...@@ -38,12 +38,31 @@
<Reference Include="System" /> <Reference Include="System" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="DescriptorUtil.cs" />
<Compile Include="EnumFieldGenerator.cs" />
<Compile Include="EnumGenerator.cs" />
<Compile Include="ExtensionGenerator.cs" />
<Compile Include="FieldGeneratorBase.cs" />
<Compile Include="IFieldSourceGenerator.cs" />
<Compile Include="ISourceGenerator.cs" />
<Compile Include="MessageFieldGenerator.cs" />
<Compile Include="MessageGenerator.cs" />
<Compile Include="PrimitiveFieldGenerator.cs" />
<Compile Include="RepeatedEnumFieldGenerator.cs" />
<Compile Include="RepeatedMessageFieldGenerator.cs" />
<Compile Include="RepeatedPrimitiveFieldGenerator.cs" />
<Compile Include="ServiceGenerator.cs" />
<Compile Include="SourceFileGenerator.cs" />
<Compile Include="DependencyResolutionException.cs" /> <Compile Include="DependencyResolutionException.cs" />
<Compile Include="Generator.cs" /> <Compile Include="Generator.cs" />
<Compile Include="GeneratorOptions.cs" /> <Compile Include="GeneratorOptions.cs" />
<Compile Include="Helpers.cs" />
<Compile Include="InvalidOptionsException.cs" /> <Compile Include="InvalidOptionsException.cs" />
<Compile Include="Program.cs" /> <Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SourceGeneratorBase.cs" />
<Compile Include="SourceGenerators.cs" />
<Compile Include="UmbrellaClassGenerator.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="app.config" /> <None Include="app.config" />
......
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<StartWorkingDirectory>c:\Users\Jon\Documents\Visual Studio 2008\Projects\ProtocolBuffers\csharp\testprotos</StartWorkingDirectory>
<StartArguments>unittest.protobin</StartArguments>
</PropertyGroup>
</Project>
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Text;
using Google.ProtocolBuffers.Descriptors;
namespace Google.ProtocolBuffers.ProtoGen {
internal class RepeatedEnumFieldGenerator : FieldGeneratorBase, IFieldSourceGenerator {
internal RepeatedEnumFieldGenerator(FieldDescriptor descriptor)
: base(descriptor) {
}
public void GenerateMembers(TextGenerator writer) {
writer.WriteLine("private pbc::PopsicleList<{0}> {1}_ = new pbc::PopsicleList<{0}>();", TypeName, Name);
writer.WriteLine("public scg::IList<{0}> {1}List {{", TypeName, CapitalizedName);
writer.WriteLine(" get {{ return pbc::Lists.AsReadOnly({0}_); }}", Name);
writer.WriteLine("}");
// TODO(jonskeet): Redundant API calls? Possibly - include for portability though. Maybe create an option.
writer.WriteLine("public int {0}Count {{", CapitalizedName);
writer.WriteLine(" get {{ return {0}_.Count; }}", Name);
writer.WriteLine("}");
writer.WriteLine("public {0} Get{1}(int index) {{", TypeName, CapitalizedName);
writer.WriteLine(" return {0}_[index];", Name);
writer.WriteLine("}");
}
public void GenerateBuilderMembers(TextGenerator writer) {
// Note: We can return the original list here, because we make it unmodifiable when we build
writer.WriteLine("public scg::IList<{0}> {1}List {{", TypeName, CapitalizedName);
writer.WriteLine(" get {{ return result.{0}_; }}", Name);
writer.WriteLine("}");
writer.WriteLine("public int {0}Count {{", CapitalizedName);
writer.WriteLine(" get {{ return result.{0}Count; }}", CapitalizedName);
writer.WriteLine("}");
writer.WriteLine("public {0} Get{1}(int index) {{", TypeName, CapitalizedName);
writer.WriteLine(" return result.Get{0}(index);", CapitalizedName);
writer.WriteLine("}");
writer.WriteLine("public Builder Set{0}(int index, {1} value) {{", CapitalizedName, TypeName);
writer.WriteLine(" result.{0}_[index] = value;", Name);
writer.WriteLine(" return this;");
writer.WriteLine("}");
writer.WriteLine("public Builder Add{0}({1} value) {{", CapitalizedName, TypeName);
writer.WriteLine(" result.{0}_.Add(value);", Name, TypeName);
writer.WriteLine(" return this;");
writer.WriteLine("}");
writer.WriteLine("public Builder AddRange{0}(scg::IEnumerable<{1}> values) {{", CapitalizedName, TypeName);
writer.WriteLine(" base.AddRange(values, result.{0}_);", Name);
writer.WriteLine(" return this;");
writer.WriteLine("}");
writer.WriteLine("public Builder Clear{0}() {{", CapitalizedName);
writer.WriteLine(" result.{0}_.Clear();", Name);
writer.WriteLine(" return this;");
writer.WriteLine("}");
}
public void GenerateMergingCode(TextGenerator writer) {
writer.WriteLine("if (other.{0}_.Count != 0) {{", Name);
writer.WriteLine(" base.AddRange(other.{0}_, result.{0}_);", Name);
writer.WriteLine("}");
}
public void GenerateBuildingCode(TextGenerator writer) {
writer.WriteLine("result.{0}_.MakeReadOnly();", Name);
}
public void GenerateParsingCode(TextGenerator writer) {
// TODO(jonskeet): Make a more efficient way of doing this
writer.WriteLine("int rawValue = input.ReadEnum();");
writer.WriteLine("if (!global::System.Enum.IsDefined(typeof({0}), rawValue)) {{", TypeName);
writer.WriteLine(" unknownFields.MergeVarintField({0}, (ulong) rawValue);", Number);
writer.WriteLine("} else {");
writer.WriteLine(" Add{0}({1} rawValue);", CapitalizedName, TypeName);
writer.WriteLine("}");
}
public void GenerateSerializationCode(TextGenerator writer) {
writer.WriteLine("foreach ({0} element in {1}List) {{", TypeName, CapitalizedName);
writer.WriteLine(" output.WriteEnum({0}, (int) element);", Number);
writer.WriteLine("}");
}
public void GenerateSerializedSizeCode(TextGenerator writer) {
writer.WriteLine("foreach ({0} element in {1}List) {{", TypeName, CapitalizedName);
writer.WriteLine(" size += pb::CodedOutputStream.ComputeEnumSize({0}, (int) element);", Number);
writer.WriteLine("}");
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
using Google.ProtocolBuffers.Descriptors;
namespace Google.ProtocolBuffers.ProtoGen {
internal class RepeatedMessageFieldGenerator : FieldGeneratorBase, IFieldSourceGenerator {
internal RepeatedMessageFieldGenerator(FieldDescriptor descriptor)
: base(descriptor) {
}
public void GenerateMembers(TextGenerator writer) {
writer.WriteLine("private pbc::PopsicleList<{0}> {1}_ = new pbc::PopsicleList<{0}>();", TypeName, Name);
writer.WriteLine("public scg::IList<{0}> {1}List {{", TypeName, CapitalizedName);
writer.WriteLine(" get {{ return {0}_; }}", Name);
writer.WriteLine("}");
// TODO(jonskeet): Redundant API calls? Possibly - include for portability though. Maybe create an option.
writer.WriteLine("public int {0}Count {{", CapitalizedName);
writer.WriteLine(" get {{ return {0}_.Count; }}", Name);
writer.WriteLine("}");
writer.WriteLine("public {0} Get{1}(int index) {{", TypeName, CapitalizedName);
writer.WriteLine(" return {0}_[index];", Name);
writer.WriteLine("}");
}
public void GenerateBuilderMembers(TextGenerator writer) {
// Note: We can return the original list here, because we make it unmodifiable when we build
writer.WriteLine("public scg::IList<{0}> {1}List {{", TypeName, CapitalizedName);
writer.WriteLine(" get {{ return result.{0}_; }}", Name);
writer.WriteLine("}");
writer.WriteLine("public int {0}Count {{", CapitalizedName);
writer.WriteLine(" get {{ return result.{0}Count; }}", CapitalizedName);
writer.WriteLine("}");
writer.WriteLine("public {0} Get{1}(int index) {{", TypeName, CapitalizedName);
writer.WriteLine(" return result.Get{0}(index);", CapitalizedName);
writer.WriteLine("}");
writer.WriteLine("public Builder Set{0}(int index, {1} value) {{", CapitalizedName, TypeName);
writer.WriteLine(" result.{0}_[index] = value;", Name);
writer.WriteLine(" return this;");
writer.WriteLine("}");
// Extra overload for builder (just on messages)
writer.WriteLine("public Builder Set{0}(int index, {1}.Builder builderForValue) {{", CapitalizedName, TypeName);
writer.WriteLine(" result.{0}_[index] = builderForValue.Build();", Name);
writer.WriteLine(" return this;");
writer.WriteLine("}");
writer.WriteLine("public Builder Add{0}({1} value) {{", CapitalizedName, TypeName);
writer.WriteLine(" result.{0}_.Add(value);", Name, TypeName);
writer.WriteLine(" return this;");
writer.WriteLine("}");
// Extra overload for builder (just on messages)
writer.WriteLine("public Builder Add{0}({1}.Builder builderForValue) {{", CapitalizedName, TypeName);
writer.WriteLine(" result.{0}_.Add(builderForValue.Build());", Name);
writer.WriteLine(" return this;");
writer.WriteLine("}");
writer.WriteLine("public Builder AddRange{0}(scg::IEnumerable<{1}> values) {{", CapitalizedName, TypeName);
writer.WriteLine(" base.AddRange(values, result.{0}_);", Name);
writer.WriteLine(" return this;");
writer.WriteLine("}");
writer.WriteLine("public Builder Clear{0}() {{", CapitalizedName);
writer.WriteLine(" result.{0}_.Clear();", Name);
writer.WriteLine(" return this;");
writer.WriteLine("}");
}
public void GenerateMergingCode(TextGenerator writer) {
writer.WriteLine("if (other.{0}_.Count != 0) {{", Name);
writer.WriteLine(" base.AddRange(other.{0}_, result.{0}_);", Name);
writer.WriteLine("}");
}
public void GenerateBuildingCode(TextGenerator writer) {
writer.WriteLine("result.{0}_.MakeReadOnly();", Name);
}
public void GenerateParsingCode(TextGenerator writer) {
writer.WriteLine("{0}.Builder subBuilder = {0}.CreateBuilder();", TypeName);
if (Descriptor.FieldType == FieldType.Group) {
writer.WriteLine("input.ReadGroup({0}, subBuilder, extensionRegistry);", Number);
} else {
writer.WriteLine("input.ReadMessage(subBuilder, extensionRegistry);");
}
writer.WriteLine("Add{0}(subBuilder.BuildPartial());", CapitalizedName);
}
public void GenerateSerializationCode(TextGenerator writer) {
writer.WriteLine("foreach ({0} element in {1}List) {{", TypeName, CapitalizedName);
writer.WriteLine(" output.Write{0}({1}, element);", MessageOrGroup, Number);
writer.WriteLine("}");
}
public void GenerateSerializedSizeCode(TextGenerator writer) {
writer.WriteLine("foreach ({0} element in {1}List) {{", TypeName, CapitalizedName);
writer.WriteLine(" size += pb::CodedOutputStream.Compute{0}Size({1}, element);", MessageOrGroup, Number);
writer.WriteLine("}");
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
using Google.ProtocolBuffers.Descriptors;
namespace Google.ProtocolBuffers.ProtoGen {
internal class RepeatedPrimitiveFieldGenerator : FieldGeneratorBase, IFieldSourceGenerator {
internal RepeatedPrimitiveFieldGenerator(FieldDescriptor descriptor)
: base(descriptor) {
}
public void GenerateMembers(TextGenerator writer) {
writer.WriteLine("private pbc::PopsicleList<{0}> {1}_ = new pbc::PopsicleList<{0}>();", TypeName, Name);
writer.WriteLine("public scg::IList<{0}> {1}List {{", TypeName, CapitalizedName);
writer.WriteLine(" get {{ return pbc::Lists.AsReadOnly({0}_); }}", Name);
writer.WriteLine("}");
// TODO(jonskeet): Redundant API calls? Possibly - include for portability though. Maybe create an option.
writer.WriteLine("public int {0}Count {{", CapitalizedName);
writer.WriteLine(" get {{ return {0}_.Count; }}", Name);
writer.WriteLine("}");
writer.WriteLine("public {0} Get{1}(int index) {{", TypeName, CapitalizedName);
writer.WriteLine(" return {0}_[index];", Name);
writer.WriteLine("}");
}
public void GenerateBuilderMembers(TextGenerator writer) {
// Note: We can return the original list here, because we make it unmodifiable when we build
writer.WriteLine("public scg::IList<{0}> {1}List {{", TypeName, CapitalizedName);
writer.WriteLine(" get {{ return result.{0}_; }}", Name);
writer.WriteLine("}");
writer.WriteLine("public int {0}Count {{", CapitalizedName);
writer.WriteLine(" get {{ return result.{0}Count; }}", CapitalizedName);
writer.WriteLine("}");
writer.WriteLine("public {0} Get{1}(int index) {{", TypeName, CapitalizedName);
writer.WriteLine(" return result.Get{0}(index);", CapitalizedName);
writer.WriteLine("}");
writer.WriteLine("public Builder Set{0}(int index, {1} value) {{", CapitalizedName, TypeName);
writer.WriteLine(" result.{0}_[index] = value;", Name);
writer.WriteLine(" return this;");
writer.WriteLine("}");
writer.WriteLine("public Builder Add{0}({1} value) {{", CapitalizedName, TypeName);
writer.WriteLine(" result.{0}_.Add(value);", Name, TypeName);
writer.WriteLine(" return this;");
writer.WriteLine("}");
writer.WriteLine("public Builder AddRange{0}(scg::IEnumerable<{1}> values) {{", CapitalizedName, TypeName);
writer.WriteLine(" base.AddRange(values, result.{0}_);", Name);
writer.WriteLine(" return this;");
writer.WriteLine("}");
writer.WriteLine("public Builder Clear{0}() {{", CapitalizedName);
writer.WriteLine(" result.{0}_.Clear();", Name);
writer.WriteLine(" return this;");
writer.WriteLine("}");
}
public void GenerateMergingCode(TextGenerator writer) {
writer.WriteLine("if (other.{0}_.Count != 0) {{", Name);
writer.WriteLine(" base.AddRange(other.{0}_, result.{0}_);", Name);
writer.WriteLine("}");
}
public void GenerateBuildingCode(TextGenerator writer) {
writer.WriteLine("result.{0}_.MakeReadOnly();", Name);
}
public void GenerateParsingCode(TextGenerator writer) {
writer.WriteLine("Add{0}(input.Read{1}());", CapitalizedName, CapitalizedTypeName);
}
public void GenerateSerializationCode(TextGenerator writer) {
writer.WriteLine("foreach ({0} element in {1}List) {{", TypeName, CapitalizedName);
writer.WriteLine(" output.Write{0}({1}, element);", CapitalizedTypeName, Number);
writer.WriteLine("}");
}
public void GenerateSerializedSizeCode(TextGenerator writer) {
writer.WriteLine("foreach ({0} element in {1}List) {{", TypeName, CapitalizedName);
writer.WriteLine(" size += pb::CodedOutputStream.Compute{0}Size({1}, element);", CapitalizedTypeName, Number);
writer.WriteLine("}");
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
using Google.ProtocolBuffers.Descriptors;
namespace Google.ProtocolBuffers.ProtoGen {
internal class ServiceGenerator : SourceGeneratorBase<ServiceDescriptor>, ISourceGenerator {
private enum RequestOrResponse {
Request,
Response
}
internal ServiceGenerator(ServiceDescriptor descriptor)
: base(descriptor) {
}
public void Generate(TextGenerator writer) {
writer.WriteLine("{0} abstract class {1} : pb::IService {{", ClassAccessLevel, Descriptor.Name);
writer.Indent();
foreach (MethodDescriptor method in Descriptor.Methods) {
writer.WriteLine("{0} abstract void {1}(", ClassAccessLevel, Helpers.UnderscoresToPascalCase(method.Name));
writer.WriteLine(" pb::IRpcController controller,");
writer.WriteLine(" {0} request,", DescriptorUtil.GetClassName(method.InputType));
writer.WriteLine(" global::System.Action<{0}> done);", DescriptorUtil.GetClassName(method.OutputType));
}
// Generate Descriptor and DescriptorForType.
writer.WriteLine();
writer.WriteLine("{0} static pbd::ServiceDescriptor Descriptor {{", ClassAccessLevel);
writer.WriteLine(" get {{ return {0}.Descriptor.Services[{1}]; }}",
DescriptorUtil.GetUmbrellaClassName(Descriptor.File), Descriptor.Index);
writer.WriteLine("}");
writer.WriteLine("{0} pbd::ServiceDescriptor DescriptorForType {{", ClassAccessLevel);
writer.WriteLine(" get { return Descriptor; }");
writer.WriteLine("}");
GenerateCallMethod(writer);
GenerateGetPrototype(RequestOrResponse.Request, writer);
GenerateGetPrototype(RequestOrResponse.Response, writer);
GenerateStub(writer);
writer.Outdent();
writer.WriteLine("}");
}
private void GenerateCallMethod(TextGenerator writer) {
writer.WriteLine();
writer.WriteLine("public void CallMethod(", ClassAccessLevel);
writer.WriteLine(" pbd::MethodDescriptor method,");
writer.WriteLine(" pb::IRpcController controller,");
writer.WriteLine(" pb::IMessage request,");
writer.WriteLine(" global::System.Action<pb::IMessage> done) {");
writer.Indent();
writer.WriteLine("if (method.Service != Descriptor) {");
writer.WriteLine(" throw new global::System.ArgumentException(");
writer.WriteLine(" \"Service.CallMethod() given method descriptor for wrong service type.\");");
writer.WriteLine("}");
writer.WriteLine("switch(method.Index) {");
writer.Indent();
foreach (MethodDescriptor method in Descriptor.Methods) {
writer.WriteLine("case {0}:", method.Index);
writer.WriteLine(" this.{0}(controller, ({0}) request,",
Helpers.UnderscoresToPascalCase(method.Name), DescriptorUtil.GetClassName(method.InputType));
writer.WriteLine(" pb::RpcUtil.SpecializeCallback<{0}>(", DescriptorUtil.GetClassName(method.OutputType));
writer.WriteLine(" done));");
writer.WriteLine(" return;");
}
writer.WriteLine("default:");
writer.WriteLine(" throw new global::System.InvalidOperationException(\"Can't get here.\");");
writer.Outdent();
writer.WriteLine("}");
writer.Outdent();
writer.WriteLine("}");
writer.WriteLine();
}
private void GenerateGetPrototype(RequestOrResponse which, TextGenerator writer) {
writer.WriteLine("public pb::IMessage Get{0}Prototype(pbd::MethodDescriptor method) {{", which);
writer.Indent();
writer.WriteLine("if (method.Service != Descriptor) {");
writer.WriteLine(" throw new global::System.ArgumentException(");
writer.WriteLine(" \"Service.Get{0}Prototype() given method descriptor for wrong service type.\");", which);
writer.WriteLine("}");
writer.WriteLine("switch(method.Index) {");
writer.Indent();
foreach (MethodDescriptor method in Descriptor.Methods) {
writer.WriteLine("case {0}:", method.Index);
writer.WriteLine(" return {0}.DefaultInstance;",
DescriptorUtil.GetClassName(which == RequestOrResponse.Request ? method.InputType : method.OutputType));
}
writer.WriteLine("default:");
writer.WriteLine(" throw new global::System.InvalidOperationException(\"Can't get here.\");");
writer.Outdent();
writer.WriteLine("}");
writer.Outdent();
writer.WriteLine("}");
writer.WriteLine();
}
private void GenerateStub(TextGenerator writer) {
writer.WriteLine("public static Stub CreateStub(pb::IRpcChannel channel) {");
writer.WriteLine(" return new Stub(channel);");
writer.WriteLine("}");
writer.WriteLine();
writer.WriteLine("{0} class Stub : {1} {{", ClassAccessLevel, DescriptorUtil.GetClassName(Descriptor));
writer.Indent();
writer.WriteLine("internal Stub(pb::IRpcChannel channel) {");
writer.WriteLine(" this.channel = channel;");
writer.WriteLine("}");
writer.WriteLine();
writer.WriteLine("private readonly pb::IRpcChannel channel;");
writer.WriteLine();
writer.WriteLine("public pb::IRpcChannel Channel {");
writer.WriteLine(" get { return channel; }");
writer.WriteLine("}");
foreach (MethodDescriptor method in Descriptor.Methods) {
writer.WriteLine();
writer.WriteLine("public override void {0}(", Helpers.UnderscoresToPascalCase(method.Name));
writer.WriteLine(" pb::IRpcController controller,");
writer.WriteLine(" {0} request,", DescriptorUtil.GetClassName(method.InputType));
writer.WriteLine(" global::System.Action<{0}> done) {{", DescriptorUtil.GetClassName(method.OutputType));
writer.Indent();
writer.WriteLine("channel.CallMethod(Descriptor.Methods[{0}],", method.Index);
writer.WriteLine(" controller, request, {0}.DefaultInstance,", DescriptorUtil.GetClassName(method.OutputType));
writer.WriteLine(" pb::RpcUtil.GeneralizeCallback<{0}, {0}.Builder>(done, {0}.DefaultInstance));",
DescriptorUtil.GetClassName(method.OutputType));
writer.Outdent();
writer.WriteLine("}");
}
writer.Outdent();
writer.WriteLine("}");
}
}
}
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Google.ProtocolBuffers.Descriptors;
namespace Google.ProtocolBuffers.ProtoGen {
/// <summary>
/// Generator to hold a TextGenerator, generate namespace aliases etc.
/// Each source file created uses one of these, and it can be used to create
/// multiple classes within the same file.
/// </summary>
internal class SourceFileGenerator {
private readonly TextGenerator output;
private SourceFileGenerator(TextWriter writer) {
output = new TextGenerator(writer);
}
/// <summary>
/// Creates a ClassFileGenerator for the given writer, which will be closed
/// when the instance is disposed. The specified namespace is created, if it's non-null.
/// </summary>
internal static SourceFileGenerator ForWriter(TextWriter writer) {
return new SourceFileGenerator(writer);
}
}
}
using System.Collections.Generic;
using Google.ProtocolBuffers.DescriptorProtos;
using Google.ProtocolBuffers.Descriptors;
namespace Google.ProtocolBuffers.ProtoGen {
internal abstract class SourceGeneratorBase<T> where T : IDescriptor {
private readonly T descriptor;
protected SourceGeneratorBase(T descriptor) {
this.descriptor = descriptor;
}
protected T Descriptor {
get { return descriptor; }
}
protected string ClassAccessLevel {
get {
// Default to public
return !descriptor.File.Options.HasExtension(CSharpOptions.CSharpPublicClasses)
|| descriptor.File.Options.GetExtension(CSharpOptions.CSharpPublicClasses) ? "public" : "internal";
}
}
public bool MultipleFiles {
get { return descriptor.File.Options.GetExtension(CSharpOptions.CSharpMultipleFiles); }
}
protected static void WriteChildren<TChild>(TextGenerator writer, string region, IEnumerable<TChild> children)
where TChild : IDescriptor {
// Copy the set of children; makes access easier
List<TChild> copy = new List<TChild>(children);
if (copy.Count == 0) {
return;
}
if (region != null) {
writer.WriteLine("#region {0}", region);
}
foreach (TChild child in children) {
SourceGenerators.CreateGenerator(child).Generate(writer);
}
if (region != null) {
writer.WriteLine("#endregion");
writer.WriteLine();
}
}
}
}
using System;
using System.Collections.Generic;
using Google.ProtocolBuffers.Descriptors;
namespace Google.ProtocolBuffers.ProtoGen {
internal static class SourceGenerators {
private static readonly Dictionary<Type, Func<IDescriptor, ISourceGenerator>> GeneratorFactories = new Dictionary<Type, Func<IDescriptor, ISourceGenerator>> {
{ typeof(FileDescriptor), descriptor => new UmbrellaClassGenerator((FileDescriptor) descriptor) },
{ typeof(EnumDescriptor), descriptor => new EnumGenerator((EnumDescriptor) descriptor) },
{ typeof(ServiceDescriptor), descriptor => new ServiceGenerator((ServiceDescriptor) descriptor) },
{ typeof(MessageDescriptor), descriptor => new MessageGenerator((MessageDescriptor) descriptor) },
// For other fields, we have IFieldSourceGenerators.
{ typeof(FieldDescriptor), descriptor => new ExtensionGenerator((FieldDescriptor) descriptor) }
};
public static IFieldSourceGenerator CreateFieldGenerator(FieldDescriptor field) {
switch (field.MappedType) {
case MappedType.Message :
return field.IsRepeated
? (IFieldSourceGenerator) new RepeatedMessageFieldGenerator(field)
: new MessageFieldGenerator(field);
case MappedType.Enum:
return field.IsRepeated
? (IFieldSourceGenerator)new RepeatedEnumFieldGenerator(field)
: new EnumFieldGenerator(field);
default:
return field.IsRepeated
? (IFieldSourceGenerator)new RepeatedPrimitiveFieldGenerator(field)
: new PrimitiveFieldGenerator(field);
}
}
public static ISourceGenerator CreateGenerator<T>(T descriptor) where T : IDescriptor {
Func<IDescriptor, ISourceGenerator> factory;
if (!GeneratorFactories.TryGetValue(typeof(T), out factory)) {
throw new ArgumentException("No generator registered for " + typeof(T).Name);
}
return factory(descriptor);
}
}
}
using System;
using Google.ProtocolBuffers.DescriptorProtos;
using Google.ProtocolBuffers.Descriptors;
namespace Google.ProtocolBuffers.ProtoGen {
/// <summary>
/// Generator for the class describing the .proto file in general,
/// containing things like the message descriptor.
/// </summary>
internal sealed class UmbrellaClassGenerator : SourceGeneratorBase<FileDescriptor>, ISourceGenerator {
internal UmbrellaClassGenerator(FileDescriptor descriptor)
: base(descriptor) {
}
public void Generate(TextGenerator writer) {
WriteIntroduction(writer);
WriteDescriptor(writer);
WriteChildren(writer, "Extensions", Descriptor.Extensions);
writer.WriteLine("#region Static variables");
foreach (MessageDescriptor message in Descriptor.MessageTypes) {
new MessageGenerator(message).GenerateStaticVariables(writer);
}
writer.WriteLine("#endregion");
// The class declaration either gets closed before or after the children are written.
if (!DescriptorUtil.NestClasses(Descriptor)) {
writer.Outdent();
writer.WriteLine("}");
}
WriteChildren(writer, "Enums", Descriptor.EnumTypes);
WriteChildren(writer, "Messages", Descriptor.MessageTypes);
WriteChildren(writer, "Services", Descriptor.Services);
if (DescriptorUtil.NestClasses(Descriptor)) {
writer.Outdent();
writer.WriteLine("}");
}
if (DescriptorUtil.GetNamespace(Descriptor) != "") {
writer.Outdent();
writer.WriteLine("}");
}
}
private void WriteIntroduction(TextGenerator writer) {
writer.WriteLine("// Generated by the protocol buffer compiler. DO NOT EDIT!");
writer.WriteLine();
Helpers.WriteNamespaces(writer);
if (DescriptorUtil.GetNamespace(Descriptor) != "") {
writer.WriteLine("namespace {0} {{", DescriptorUtil.GetNamespace(Descriptor));
writer.Indent();
writer.WriteLine();
}
writer.WriteLine("{0} static partial class {1} {{", ClassAccessLevel, DescriptorUtil.GetUmbrellaClassName(Descriptor));
writer.WriteLine();
writer.Indent();
}
private void WriteDescriptor(TextGenerator writer) {
writer.WriteLine("#region Descriptor");
writer.WriteLine("public static pbd::FileDescriptor Descriptor {");
writer.WriteLine(" get { return descriptor; }");
writer.WriteLine("}");
writer.WriteLine("private static readonly pbd::FileDescriptor descriptor = pbd::FileDescriptor.InternalBuildGeneratedFileFrom(");
writer.WriteLine(" global::System.Convert.FromBase64String(");
writer.Indent();
writer.Indent();
// TODO(jonskeet): Consider a C#-escaping format here instead of just Base64.
byte[] bytes = Descriptor.Proto.ToByteArray();
string base64 = Convert.ToBase64String(bytes);
while (base64.Length > 60) {
writer.WriteLine("\"{0}\" + ", base64.Substring(0, 60));
base64 = base64.Substring(60);
}
writer.WriteLine("\"{0}\"),", base64);
writer.WriteLine("new pbd::FileDescriptor[] {");
foreach (FileDescriptor dependency in Descriptor.Dependencies) {
// TODO(jonskeet): The normal code won't work for the bootstrapping descriptor, because we don't get unknown fields :(
if (dependency.Package == "google.protobuf" && dependency.Name.EndsWith("descriptor.proto")) {
writer.WriteLine(" global::" + typeof(DescriptorProtoFile).FullName + ".Descriptor, ");
continue;
}
writer.WriteLine(" {0}.Descriptor, ", DescriptorUtil.GetFullUmbrellaClassName(dependency));
}
writer.WriteLine("});");
writer.Outdent();
writer.Outdent();
writer.WriteLine("#endregion");
writer.WriteLine();
}
}
}
// Generated by the protocol buffer compiler. DO NOT EDIT!
using pb = global::Google.ProtocolBuffers;
using pbc = global::Google.ProtocolBuffers.Collections;
using pbd = global::Google.ProtocolBuffers.Descriptors;
using scg = global::System.Collections.Generic;
namespace Google.ProtocolBuffers.DescriptorProtos {
public static partial class CSharpOptions {
#region Descriptor
public static pbd::FileDescriptor Descriptor {
get { return descriptor; }
}
private static readonly pbd::FileDescriptor descriptor = pbd::FileDescriptor.InternalBuildGeneratedFileFrom(
global::System.Convert.FromBase64String(
"CiRnb29nbGUvcHJvdG9idWYvY3NoYXJwX29wdGlvbnMucHJvdG8SD2dvb2ds" +
"ZS5wcm90b2J1ZhogZ29vZ2xlL3Byb3RvYnVmL2Rlc2NyaXB0b3IucHJvdG86" +
"NwoPQ1NoYXJwTmFtZXNwYWNlEhwuZ29vZ2xlLnByb3RvYnVmLkZpbGVPcHRp" +
"b25zGKCcASABKAk6PwoXQ1NoYXJwVW1icmVsbGFDbGFzc25hbWUSHC5nb29n" +
"bGUucHJvdG9idWYuRmlsZU9wdGlvbnMYoZwBIAEoCTo7ChNDU2hhcnBNdWx0" +
"aXBsZUZpbGVzEhwuZ29vZ2xlLnByb3RvYnVmLkZpbGVPcHRpb25zGKKcASAB" +
"KAg6OQoRQ1NoYXJwTmVzdENsYXNzZXMSHC5nb29nbGUucHJvdG9idWYuRmls" +
"ZU9wdGlvbnMYo5wBIAEoCDo7ChNDU2hhcnBQdWJsaWNDbGFzc2VzEhwuZ29v" +
"Z2xlLnByb3RvYnVmLkZpbGVPcHRpb25zGKScASABKAhCPILiCSdHb29nbGUu" +
"UHJvdG9jb2xCdWZmZXJzLkRlc2NyaXB0b3JQcm90b3OK4gkNQ1NoYXJwT3B0" +
"aW9ucw=="),
new pbd::FileDescriptor[] {
global::Google.ProtocolBuffers.DescriptorProtos.DescriptorProtoFile.Descriptor,
});
#endregion
#region Extensions
public static readonly pb::GeneratedExtensionBase<string> CSharpNamespace =
pb::GeneratedSingleExtension<string>.CreateInstance(Descriptor.Extensions[0]);
public static readonly pb::GeneratedExtensionBase<string> CSharpUmbrellaClassname =
pb::GeneratedSingleExtension<string>.CreateInstance(Descriptor.Extensions[1]);
public static readonly pb::GeneratedExtensionBase<bool> CSharpMultipleFiles =
pb::GeneratedSingleExtension<bool>.CreateInstance(Descriptor.Extensions[2]);
public static readonly pb::GeneratedExtensionBase<bool> CSharpNestClasses =
pb::GeneratedSingleExtension<bool>.CreateInstance(Descriptor.Extensions[3]);
public static readonly pb::GeneratedExtensionBase<bool> CSharpPublicClasses =
pb::GeneratedSingleExtension<bool>.CreateInstance(Descriptor.Extensions[4]);
#endregion
#region Static variables
#endregion
}
}
...@@ -18,6 +18,21 @@ ...@@ -18,6 +18,21 @@
// autogenerated classes, so that they implement // autogenerated classes, so that they implement
// IDescriptorProto // IDescriptorProto
namespace Google.ProtocolBuffers.DescriptorProtos { namespace Google.ProtocolBuffers.DescriptorProtos {
// TODO(jonskeet): Find a better way of fixing this. It's needed in order to
// cope with unknown fields during initialization.
public partial class DescriptorProtoFile {
private static readonly bool initialized = false;
internal static bool Bootstrapping {
get { return !initialized; }
}
static DescriptorProtoFile() {
initialized = true;
}
}
public partial class DescriptorProto : IDescriptorProto<MessageOptions> { } public partial class DescriptorProto : IDescriptorProto<MessageOptions> { }
public partial class EnumDescriptorProto : IDescriptorProto<EnumOptions> { } public partial class EnumDescriptorProto : IDescriptorProto<EnumOptions> { }
public partial class EnumValueDescriptorProto : IDescriptorProto<EnumValueOptions> { } public partial class EnumValueDescriptorProto : IDescriptorProto<EnumValueOptions> { }
......
...@@ -181,7 +181,7 @@ namespace Google.ProtocolBuffers.Descriptors { ...@@ -181,7 +181,7 @@ namespace Google.ProtocolBuffers.Descriptors {
/// a valid descriptor. This can occur for a number of reasons, such as a field /// a valid descriptor. This can occur for a number of reasons, such as a field
/// having an undefined type or because two messages were defined with the same name.</exception> /// having an undefined type or because two messages were defined with the same name.</exception>
public static FileDescriptor BuildFrom(FileDescriptorProto proto, FileDescriptor[] dependencies) { public static FileDescriptor BuildFrom(FileDescriptorProto proto, FileDescriptor[] dependencies) {
// Building decsriptors involves two steps: translating and linking. // Building descriptors involves two steps: translating and linking.
// In the translation step (implemented by FileDescriptor's // In the translation step (implemented by FileDescriptor's
// constructor), we build an object tree mirroring the // constructor), we build an object tree mirroring the
// FileDescriptorProto's tree and put all of the descriptors into the // FileDescriptorProto's tree and put all of the descriptors into the
...@@ -204,9 +204,9 @@ namespace Google.ProtocolBuffers.Descriptors { ...@@ -204,9 +204,9 @@ namespace Google.ProtocolBuffers.Descriptors {
} }
for (int i = 0; i < proto.DependencyCount; i++) { for (int i = 0; i < proto.DependencyCount; i++) {
if (dependencies[i].Name != proto.DependencyList[i]) { if (dependencies[i].Name != proto.DependencyList[i]) {
throw new DescriptorValidationException(result, /*throw new DescriptorValidationException(result,
"Dependencies passed to FileDescriptor.BuildFrom() don't match " + "Dependencies passed to FileDescriptor.BuildFrom() don't match " +
"those listed in the FileDescriptorProto."); "those listed in the FileDescriptorProto.");*/
} }
} }
......
...@@ -48,7 +48,7 @@ namespace Google.ProtocolBuffers.FieldAccess { ...@@ -48,7 +48,7 @@ namespace Google.ProtocolBuffers.FieldAccess {
PropertyInfo messageProperty = typeof(TMessage).GetProperty(name + "List"); PropertyInfo messageProperty = typeof(TMessage).GetProperty(name + "List");
PropertyInfo builderProperty = typeof(TBuilder).GetProperty(name + "List"); PropertyInfo builderProperty = typeof(TBuilder).GetProperty(name + "List");
PropertyInfo countProperty = typeof(TMessage).GetProperty(name + "Count"); PropertyInfo countProperty = typeof(TMessage).GetProperty(name + "Count");
MethodInfo clearMethod = typeof(TBuilder).GetMethod("Clear" + name); MethodInfo clearMethod = typeof(TBuilder).GetMethod("Clear" + name, Type.EmptyTypes);
getElementMethod = typeof(TMessage).GetMethod("Get" + name, new Type[] { typeof(int) }); getElementMethod = typeof(TMessage).GetMethod("Get" + name, new Type[] { typeof(int) });
clrType = getElementMethod.ReturnType; clrType = getElementMethod.ReturnType;
MethodInfo addMethod = typeof(TBuilder).GetMethod("Add" + name, new Type[] { ClrType }); MethodInfo addMethod = typeof(TBuilder).GetMethod("Add" + name, new Type[] { ClrType });
......
...@@ -39,10 +39,13 @@ namespace Google.ProtocolBuffers.FieldAccess { ...@@ -39,10 +39,13 @@ namespace Google.ProtocolBuffers.FieldAccess {
} }
internal SinglePrimitiveAccessor(string name) { internal SinglePrimitiveAccessor(string name) {
PropertyInfo messageProperty = typeof(TMessage).GetProperty(name);
PropertyInfo builderProperty = typeof(TBuilder).GetProperty(name); string propertyName = name == typeof(TMessage).Name ? name + "_" : name;
PropertyInfo messageProperty = typeof(TMessage).GetProperty(propertyName);
PropertyInfo builderProperty = typeof(TBuilder).GetProperty(name); // FIXME!
if (builderProperty == null) builderProperty = typeof(TBuilder).GetProperty(propertyName); // FIXME!
PropertyInfo hasProperty = typeof(TMessage).GetProperty("Has" + name); PropertyInfo hasProperty = typeof(TMessage).GetProperty("Has" + name);
MethodInfo clearMethod = typeof(TBuilder).GetMethod("Clear" + name); MethodInfo clearMethod = typeof(TBuilder).GetMethod("Clear" + name, Type.EmptyTypes);
if (messageProperty == null || builderProperty == null || hasProperty == null || clearMethod == null) { if (messageProperty == null || builderProperty == null || hasProperty == null || clearMethod == null) {
throw new ArgumentException("Not all required properties/methods available"); throw new ArgumentException("Not all required properties/methods available");
} }
......
...@@ -33,6 +33,7 @@ namespace Google.ProtocolBuffers { ...@@ -33,6 +33,7 @@ namespace Google.ProtocolBuffers {
/// be impossible to guarantee if this were a public class, of course. /// be impossible to guarantee if this were a public class, of course.
/// ///
/// All repeated fields are stored as IList[object] even /// All repeated fields are stored as IList[object] even
/// TODO(jonskeet): Finish this comment!
/// </summary> /// </summary>
internal sealed class FieldSet { internal sealed class FieldSet {
......
...@@ -48,6 +48,7 @@ ...@@ -48,6 +48,7 @@
<Compile Include="Collections\Dictionaries.cs" /> <Compile Include="Collections\Dictionaries.cs" />
<Compile Include="Collections\Lists.cs" /> <Compile Include="Collections\Lists.cs" />
<Compile Include="Collections\ReadOnlyDictionary.cs" /> <Compile Include="Collections\ReadOnlyDictionary.cs" />
<Compile Include="DescriptorProtos\CSharpOptions.cs" />
<Compile Include="DescriptorProtos\DescriptorProtoFile.cs" /> <Compile Include="DescriptorProtos\DescriptorProtoFile.cs" />
<Compile Include="DescriptorProtos\IDescriptorProto.cs" /> <Compile Include="DescriptorProtos\IDescriptorProto.cs" />
<Compile Include="DescriptorProtos\PartialClasses.cs" /> <Compile Include="DescriptorProtos\PartialClasses.cs" />
......
...@@ -97,6 +97,10 @@ namespace Google.ProtocolBuffers { ...@@ -97,6 +97,10 @@ namespace Google.ProtocolBuffers {
Write(text.Substring(pos)); Write(text.Substring(pos));
} }
public void Write(string format, params object[] args) {
Write(string.Format(format, args));
}
private void Write(string data) { private void Write(string data) {
if (data.Length == 0) { if (data.Length == 0) {
return; return;
......
...@@ -18,6 +18,7 @@ using System.Collections.Generic; ...@@ -18,6 +18,7 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using Google.ProtocolBuffers.Collections; using Google.ProtocolBuffers.Collections;
using Google.ProtocolBuffers.Descriptors; using Google.ProtocolBuffers.Descriptors;
using Google.ProtocolBuffers.DescriptorProtos;
namespace Google.ProtocolBuffers { namespace Google.ProtocolBuffers {
/// <summary> /// <summary>
...@@ -454,6 +455,11 @@ namespace Google.ProtocolBuffers { ...@@ -454,6 +455,11 @@ namespace Google.ProtocolBuffers {
/// <returns>true unless the tag is an end-group tag</returns> /// <returns>true unless the tag is an end-group tag</returns>
internal bool MergeFieldFrom(CodedInputStream input, internal bool MergeFieldFrom(CodedInputStream input,
ExtensionRegistry extensionRegistry, IBuilder builder, uint tag) { ExtensionRegistry extensionRegistry, IBuilder builder, uint tag) {
if (DescriptorProtoFile.Bootstrapping) {
return MergeFieldFrom(tag, input);
}
MessageDescriptor type = builder.DescriptorForType; MessageDescriptor type = builder.DescriptorForType;
if (type.Options.MessageSetWireFormat && tag == WireFormat.MessageSetTag.ItemStart) { if (type.Options.MessageSetWireFormat && tag == WireFormat.MessageSetTag.ItemStart) {
MergeMessageSetExtensionFromCodedStream(input, extensionRegistry, builder); MergeMessageSetExtensionFromCodedStream(input, extensionRegistry, builder);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment