Browse Source

Final commit before changing layout

Jon Skeet 17 năm trước cách đây
mục cha
commit
e60ce8bfaf
39 tập tin đã thay đổi với 2406 bổ sung444 xóa
  1. 80 2
      csharp/ProtoGen.Test/DependencyResolutionTest.cs
  2. 69 0
      csharp/ProtoGen.Test/DescriptorUtilTest.cs
  3. 6 3
      csharp/ProtoGen.Test/GeneratorTest.cs
  4. 33 0
      csharp/ProtoGen.Test/HelpersTest.cs
  5. 5 1
      csharp/ProtoGen.Test/ProtoGen.Test.csproj
  6. 108 0
      csharp/ProtoGen/DescriptorUtil.cs
  7. 75 0
      csharp/ProtoGen/EnumFieldGenerator.cs
  8. 19 0
      csharp/ProtoGen/EnumGenerator.cs
  9. 37 0
      csharp/ProtoGen/ExtensionGenerator.cs
  10. 130 0
      csharp/ProtoGen/FieldGeneratorBase.cs
  11. 96 5
      csharp/ProtoGen/Generator.cs
  12. 78 0
      csharp/ProtoGen/Helpers.cs
  13. 11 0
      csharp/ProtoGen/IFieldSourceGenerator.cs
  14. 5 0
      csharp/ProtoGen/ISourceGenerator.cs
  15. 92 0
      csharp/ProtoGen/MessageFieldGenerator.cs
  16. 453 0
      csharp/ProtoGen/MessageGenerator.cs
  17. 70 0
      csharp/ProtoGen/PrimitiveFieldGenerator.cs
  18. 11 5
      csharp/ProtoGen/Program.cs
  19. 19 0
      csharp/ProtoGen/ProtoGen.csproj
  20. 6 0
      csharp/ProtoGen/ProtoGen.csproj.user
  21. 90 0
      csharp/ProtoGen/RepeatedEnumFieldGenerator.cs
  22. 100 0
      csharp/ProtoGen/RepeatedMessageFieldGenerator.cs
  23. 84 0
      csharp/ProtoGen/RepeatedPrimitiveFieldGenerator.cs
  24. 138 0
      csharp/ProtoGen/ServiceGenerator.cs
  25. 29 0
      csharp/ProtoGen/SourceFileGenerator.cs
  26. 50 0
      csharp/ProtoGen/SourceGeneratorBase.cs
  27. 42 0
      csharp/ProtoGen/SourceGenerators.cs
  28. 96 0
      csharp/ProtoGen/UmbrellaClassGenerator.cs
  29. 45 71
      csharp/ProtocolBuffers.Test/TestProtos/UnitTestImportProtoFile.cs
  30. 49 0
      csharp/ProtocolBuffers/DescriptorProtos/CSharpOptions.cs
  31. 243 350
      csharp/ProtocolBuffers/DescriptorProtos/DescriptorProtoFile.cs
  32. 15 0
      csharp/ProtocolBuffers/DescriptorProtos/PartialClasses.cs
  33. 3 3
      csharp/ProtocolBuffers/Descriptors/FileDescriptor.cs
  34. 1 1
      csharp/ProtocolBuffers/FieldAccess/RepeatedPrimitiveAccessor.cs
  35. 6 3
      csharp/ProtocolBuffers/FieldAccess/SinglePrimitiveAccessor.cs
  36. 1 0
      csharp/ProtocolBuffers/FieldSet.cs
  37. 1 0
      csharp/ProtocolBuffers/ProtocolBuffers.csproj
  38. 4 0
      csharp/ProtocolBuffers/TextGenerator.cs
  39. 6 0
      csharp/ProtocolBuffers/UnknownFieldSet.cs

+ 80 - 2
csharp/ProtoGen.Test/DependencyResolutionTest.cs

@@ -1,9 +1,12 @@
 using System;
 using System.Collections.Generic;
 using System.Text;
+using Google.ProtocolBuffers.Descriptors;
 using NUnit.Framework;
+using Google.ProtocolBuffers.DescriptorProtos;
+using Google.ProtocolBuffers.ProtoGen;
 
-namespace ProtoGen {
+namespace Google.ProtocolBuffers.ProtoGen {
   /// <summary>
   /// Tests for the dependency resolution in Generator.
   /// </summary>
@@ -12,6 +15,81 @@ namespace ProtoGen {
 
     [Test]
     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
+      }
     }
   }
-}
+}

+ 69 - 0
csharp/ProtoGen.Test/DescriptorUtilTest.cs

@@ -0,0 +1,69 @@
+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));
+    }
+  }
+}

+ 6 - 3
csharp/ProtoGen.Test/GeneratorTest.cs

@@ -1,7 +1,10 @@
-using NUnit.Framework;
+using Google.ProtocolBuffers.DescriptorProtos;
+using NUnit.Framework;
+using Google.ProtocolBuffers.Descriptors;
 
-namespace ProtoGen {
+namespace Google.ProtocolBuffers.ProtoGen {
   [TestFixture]
   public class GeneratorTest {
+
   }
-}
+}

+ 33 - 0
csharp/ProtoGen.Test/HelpersTest.cs

@@ -0,0 +1,33 @@
+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);
+    }
+  }
+}

+ 5 - 1
csharp/ProtoGen.Test/ProtoGen.Test.csproj

@@ -8,7 +8,7 @@
     <ProjectGuid>{C268DA4C-4004-47DA-AF23-44C983281A68}</ProjectGuid>
     <OutputType>Library</OutputType>
     <AppDesignerFolder>Properties</AppDesignerFolder>
-    <RootNamespace>ProtoGen</RootNamespace>
+    <RootNamespace>Google.ProtocolBuffers.ProtoGen</RootNamespace>
     <AssemblyName>Google.ProtocolBuffers.ProtoGen.Test</AssemblyName>
     <TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
     <FileAlignment>512</FileAlignment>
@@ -42,10 +42,14 @@
       <HintPath>..\lib\Rhino.Mocks.dll</HintPath>
     </Reference>
     <Reference Include="System" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Xml" />
   </ItemGroup>
   <ItemGroup>
     <Compile Include="DependencyResolutionTest.cs" />
+    <Compile Include="DescriptorUtilTest.cs" />
     <Compile Include="GeneratorTest.cs" />
+    <Compile Include="HelpersTest.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
   </ItemGroup>
   <ItemGroup>

+ 108 - 0
csharp/ProtoGen/DescriptorUtil.cs

@@ -0,0 +1,108 @@
+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);
+      }
+    }
+  }
+}

+ 75 - 0
csharp/ProtoGen/EnumFieldGenerator.cs

@@ -0,0 +1,75 @@
+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("}");    
+    }
+  }
+}

+ 19 - 0
csharp/ProtoGen/EnumGenerator.cs

@@ -0,0 +1,19 @@
+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();
+    }
+  }
+}

+ 37 - 0
csharp/ProtoGen/ExtensionGenerator.cs

@@ -0,0 +1,37 @@
+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);
+      }
+    }
+  }
+}

+ 130 - 0
csharp/ProtoGen/FieldGeneratorBase.cs

@@ -0,0 +1,130 @@
+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();
+      }
+    }
+  }
+}

+ 96 - 5
csharp/ProtoGen/Generator.cs

@@ -1,8 +1,10 @@
 using System;
 using System.Collections.Generic;
+using System.Text;
 using Google.ProtocolBuffers.DescriptorProtos;
 using System.IO;
 using Google.ProtocolBuffers.Descriptors;
+using Google.ProtocolBuffers.Collections;
 
 namespace Google.ProtocolBuffers.ProtoGen {
   /// <summary>
@@ -26,22 +28,111 @@ namespace Google.ProtocolBuffers.ProtoGen {
 
     public void Generate() {
       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)) {
-          descriptorProtos = FileDescriptorSet.ParseFrom(inputStream);
+          descriptorProtos = FileDescriptorSet.ParseFrom(inputStream, extensionRegistry);
         }
-        List<FileDescriptor> descriptors = ConvertDescriptors(descriptorProtos);
+        IList<FileDescriptor> descriptors = ConvertDescriptors(descriptorProtos);
+
+        foreach (FileDescriptor descriptor in descriptors) {
+          Generate(descriptor);
+        }
+      }
+    }
+
+    /// <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>
     /// 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.
     /// Note: this method is internal rather than private to allow testing.
     /// </summary>
     /// <exception cref="DependencyResolutionException">Not all dependencies could be resolved.</exception>
-    internal static List<FileDescriptor> ConvertDescriptors(FileDescriptorSet descriptorProtos) {
-      return null;
+    internal static IList<FileDescriptor> ConvertDescriptors(FileDescriptorSet descriptorProtos) {
+      // 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);
     }
   }
 }

+ 78 - 0
csharp/ProtoGen/Helpers.cs

@@ -0,0 +1,78 @@
+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;
+    }
+  }
+}

+ 11 - 0
csharp/ProtoGen/IFieldSourceGenerator.cs

@@ -0,0 +1,11 @@
+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);
+  }
+}

+ 5 - 0
csharp/ProtoGen/ISourceGenerator.cs

@@ -0,0 +1,5 @@
+namespace Google.ProtocolBuffers.ProtoGen {
+  internal interface ISourceGenerator {
+    void Generate(TextGenerator writer);
+  }
+}

+ 92 - 0
csharp/ProtoGen/MessageFieldGenerator.cs

@@ -0,0 +1,92 @@
+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("}");
+    }
+  }
+}

+ 453 - 0
csharp/ProtoGen/MessageGenerator.cs

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

+ 70 - 0
csharp/ProtoGen/PrimitiveFieldGenerator.cs

@@ -0,0 +1,70 @@
+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("}");
+    }
+  }
+}

+ 11 - 5
csharp/ProtoGen/Program.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Collections.Generic;
+using Google.ProtocolBuffers.DescriptorProtos;
 
 namespace Google.ProtocolBuffers.ProtoGen {
   /// <summary>
@@ -8,7 +9,8 @@ namespace Google.ProtocolBuffers.ProtoGen {
   class Program {
     static int Main(string[] args) {
       try {
-
+        // Hack to make sure everything's initialized
+        DescriptorProtoFile.Descriptor.ToString();
         GeneratorOptions options = ParseCommandLineArguments(args);
 
         IList<string> validationFailures;
@@ -25,16 +27,20 @@ namespace Google.ProtocolBuffers.ProtoGen {
 
         return 0;
       } 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;
       }
     }
 
     private static GeneratorOptions ParseCommandLineArguments(string[] args) {
       GeneratorOptions options = new GeneratorOptions();
-      string baseDir = "c:\\Users\\Jon\\Documents\\Visual Studio 2008\\Projects\\ProtocolBuffers";
-      options.OutputDirectory = baseDir + "\\tmp";
-      options.InputFiles = new[] { baseDir + "\\protos\\nwind.protobin" };
+      //string baseDir = "c:\\Users\\Jon\\Documents\\Visual Studio 2008\\Projects\\ProtocolBuffers";
+      //options.OutputDirectory = baseDir + "\\tmp";
+      //options.InputFiles = new[] { baseDir + "\\protos\\nwind-solo.protobin" };
+      options.OutputDirectory = ".";
+      options.InputFiles = args;
       return options;
     }
   }

+ 19 - 0
csharp/ProtoGen/ProtoGen.csproj

@@ -38,12 +38,31 @@
     <Reference Include="System" />
   </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="Generator.cs" />
     <Compile Include="GeneratorOptions.cs" />
+    <Compile Include="Helpers.cs" />
     <Compile Include="InvalidOptionsException.cs" />
     <Compile Include="Program.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="SourceGeneratorBase.cs" />
+    <Compile Include="SourceGenerators.cs" />
+    <Compile Include="UmbrellaClassGenerator.cs" />
   </ItemGroup>
   <ItemGroup>
     <None Include="app.config" />

+ 6 - 0
csharp/ProtoGen/ProtoGen.csproj.user

@@ -0,0 +1,6 @@
+<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>

+ 90 - 0
csharp/ProtoGen/RepeatedEnumFieldGenerator.cs

@@ -0,0 +1,90 @@
+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("}");
+    }
+  }
+}

+ 100 - 0
csharp/ProtoGen/RepeatedMessageFieldGenerator.cs

@@ -0,0 +1,100 @@
+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("}");
+    }
+  }
+}

+ 84 - 0
csharp/ProtoGen/RepeatedPrimitiveFieldGenerator.cs

@@ -0,0 +1,84 @@
+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("}");
+    }
+  }
+}

+ 138 - 0
csharp/ProtoGen/ServiceGenerator.cs

@@ -0,0 +1,138 @@
+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("}");
+    }
+  }
+}

+ 29 - 0
csharp/ProtoGen/SourceFileGenerator.cs

@@ -0,0 +1,29 @@
+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);
+    }
+  }
+}

+ 50 - 0
csharp/ProtoGen/SourceGeneratorBase.cs

@@ -0,0 +1,50 @@
+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();
+      }
+    }
+  }
+}

+ 42 - 0
csharp/ProtoGen/SourceGenerators.cs

@@ -0,0 +1,42 @@
+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);
+    }
+  }
+}

+ 96 - 0
csharp/ProtoGen/UmbrellaClassGenerator.cs

@@ -0,0 +1,96 @@
+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();
+    }
+  }
+}

+ 45 - 71
csharp/ProtocolBuffers.Test/TestProtos/UnitTestImportProtoFile.cs

@@ -10,40 +10,31 @@ namespace Google.ProtocolBuffers.TestProtos {
   
     #region Descriptor
     public static pbd::FileDescriptor Descriptor {
-        get { return descriptor; }
-    }
-    private static readonly pbd::FileDescriptor descriptor = pbd::FileDescriptor.InternalBuildGeneratedFileFrom (
-        new byte[] {
-            0x0a, 0x25, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x75, 0x6e, 
-            0x69, 0x74, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 
-            0x18, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x5f, 0x75, 0x6e, 0x69, 0x74, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x69, 
-            0x6d, 0x70, 0x6f, 0x72, 0x74, 0x22, 0x1a, 0x0a, 0x0d, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 
-            0x67, 0x65, 0x12, 0x09, 0x0a, 0x01, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x2a, 0x3c, 0x0a, 0x0a, 0x49, 0x6d, 0x70, 
-            0x6f, 0x72, 0x74, 0x45, 0x6e, 0x75, 0x6d, 0x12, 0x0e, 0x0a, 0x0a, 0x49, 0x4d, 0x50, 0x4f, 0x52, 0x54, 0x5f, 0x46, 0x4f, 
-            0x4f, 0x10, 0x07, 0x12, 0x0e, 0x0a, 0x0a, 0x49, 0x4d, 0x50, 0x4f, 0x52, 0x54, 0x5f, 0x42, 0x41, 0x52, 0x10, 0x08, 0x12, 
-            0x0e, 0x0a, 0x0a, 0x49, 0x4d, 0x50, 0x4f, 0x52, 0x54, 0x5f, 0x42, 0x41, 0x5a, 0x10, 0x09, 0x42, 0x5a, 0x0a, 0x18, 0x63, 
-            0x6f, 0x6d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x74, 
-            0x65, 0x73, 0x74, 0x48, 0x01, 0xc2, 0x3e, 0x21, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 
-            0x63, 0x6f, 0x6c, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x73, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 
-            0x73, 0xca, 0x3e, 0x17, 0x55, 0x6e, 0x69, 0x74, 0x54, 0x65, 0x73, 0x74, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x50, 0x72, 
-            0x6f, 0x74, 0x6f, 0x46, 0x69, 0x6c, 0x65, 
-        }, new pbd::FileDescriptor[] {
+      get { return descriptor; }
+    }
+    private static readonly pbd::FileDescriptor descriptor = pbd::FileDescriptor.InternalBuildGeneratedFileFrom(
+        global::System.Convert.FromBase64String(
+        "ChV1bml0dGVzdF9pbXBvcnQucHJvdG8SGHByb3RvYnVmX3VuaXR0ZXN0X2lt" + 
+        "cG9ydBokZ29vZ2xlL3Byb3RvYnVmL2NzaGFycF9vcHRpb25zLnByb3RvGiBn" + 
+        "b29nbGUvcHJvdG9idWYvZGVzY3JpcHRvci5wcm90byIaCg1JbXBvcnRNZXNz" + 
+        "YWdlEgkKAWQYASABKAUqPAoKSW1wb3J0RW51bRIOCgpJTVBPUlRfRk9PEAcS" + 
+        "DgoKSU1QT1JUX0JBUhAIEg4KCklNUE9SVF9CQVoQCUJcChhjb20uZ29vZ2xl" + 
+        "LnByb3RvYnVmLnRlc3RIAYLiCSFHb29nbGUuUHJvdG9jb2xCdWZmZXJzLlRl" + 
+        "c3RQcm90b3OK4gkXVW5pdFRlc3RJbXBvcnRQcm90b0ZpbGU="),
+        new pbd::FileDescriptor[] {
+          global::Google.ProtocolBuffers.DescriptorProtos.CSharpOptions.Descriptor, 
+          global::Google.ProtocolBuffers.DescriptorProtos.DescriptorProtoFile.Descriptor, 
         });
     #endregion
     
-    #region Extensions
-    #endregion
-    
     #region Static variables
-    internal static readonly pbd::MessageDescriptor internal__static_protobuf_unittest_import_ImportMessage__Descriptor 
+    internal static readonly pbd::MessageDescriptor internal__static_protobuf_unittest_import_ImportMessage__Descriptor
         = Descriptor.MessageTypes[0];
     internal static pb::FieldAccess.FieldAccessorTable<global::Google.ProtocolBuffers.TestProtos.ImportMessage, global::Google.ProtocolBuffers.TestProtos.ImportMessage.Builder> internal__static_protobuf_unittest_import_ImportMessage__FieldAccessorTable
         = new pb::FieldAccess.FieldAccessorTable<global::Google.ProtocolBuffers.TestProtos.ImportMessage, global::Google.ProtocolBuffers.TestProtos.ImportMessage.Builder>(internal__static_protobuf_unittest_import_ImportMessage__Descriptor,
             new string[] { "D", });
     #endregion
-    
   }
-  
   #region Enums
   public enum ImportEnum {
     IMPORT_FOO = 7,
@@ -76,7 +67,6 @@ namespace Google.ProtocolBuffers.TestProtos {
       get { return global::Google.ProtocolBuffers.TestProtos.UnitTestImportProtoFile.internal__static_protobuf_unittest_import_ImportMessage__FieldAccessorTable; }
     }
     
-    // optional int32 d = 1;
     private bool hasD;
     private int d_ = 0;
     public bool HasD {
@@ -115,62 +105,50 @@ namespace Google.ProtocolBuffers.TestProtos {
       }
     }
     
-    public static global::Google.ProtocolBuffers.TestProtos.ImportMessage ParseFrom(pb::ByteString data) {
+    public static ImportMessage ParseFrom(pb::ByteString data) {
       return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed();
     }
-    public static global::Google.ProtocolBuffers.TestProtos.ImportMessage ParseFrom(pb::ByteString data,
-        pb::ExtensionRegistry extensionRegistry) {
-      return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry))
-               .BuildParsed();
+    public static ImportMessage ParseFrom(pb::ByteString data, pb::ExtensionRegistry extensionRegistry) {
+      return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();
     }
-    public static global::Google.ProtocolBuffers.TestProtos.ImportMessage ParseFrom(byte[] data) {
+    public static ImportMessage ParseFrom(byte[] data) {
       return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed();
     }
-    public static global::Google.ProtocolBuffers.TestProtos.ImportMessage ParseFrom(byte[] data,
-        pb::ExtensionRegistry extensionRegistry) {
-      return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry))
-               .BuildParsed();
+    public static ImportMessage ParseFrom(byte[] data, pb::ExtensionRegistry extensionRegistry) {
+      return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();
     }
-    public static global::Google.ProtocolBuffers.TestProtos.ImportMessage ParseFrom(global::System.IO.Stream input) {
+    public static ImportMessage ParseFrom(global::System.IO.Stream input) {
       return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed();
     }
-    public static global::Google.ProtocolBuffers.TestProtos.ImportMessage ParseFrom(
-        global::System.IO.Stream input,
-        pb::ExtensionRegistry extensionRegistry) {
-      return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry))
-               .BuildParsed();
+    public static ImportMessage ParseFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) {
+      return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();
     }
-    public static global::Google.ProtocolBuffers.TestProtos.ImportMessage ParseFrom(pb::CodedInputStream input) {
+    public static ImportMessage ParseFrom(pb::CodedInputStream input) {
       return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed();
     }
-    public static global::Google.ProtocolBuffers.TestProtos.ImportMessage ParseFrom(pb::CodedInputStream input,
-        pb::ExtensionRegistry extensionRegistry) {
-      return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry))
-               .BuildParsed();
+    public static ImportMessage ParseFrom(pb::CodedInputStream input, pb::ExtensionRegistry extensionRegistry) {
+      return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();
     }
-    
     public static Builder CreateBuilder() { return new Builder(); }
     public override Builder CreateBuilderForType() { return new Builder(); }
-    public static Builder CreateBuilder(global::Google.ProtocolBuffers.TestProtos.ImportMessage prototype) {
+    public static Builder CreateBuilder(ImportMessage prototype) {
       return (Builder) new Builder().MergeFrom(prototype);
     }
     
-    public sealed partial class Builder : pb::GeneratedBuilder<global::Google.ProtocolBuffers.TestProtos.ImportMessage, Builder> {
+    public sealed partial class Builder : pb::GeneratedBuilder<ImportMessage, Builder> {
       protected override Builder ThisBuilder {
         get { return this; }
       }
-      
-      // Construct using global::Google.ProtocolBuffers.TestProtos.ImportMessage.CreateBuilder()
       public Builder() {}
       
-      global::Google.ProtocolBuffers.TestProtos.ImportMessage result = new global::Google.ProtocolBuffers.TestProtos.ImportMessage();
+      ImportMessage result = new ImportMessage();
       
-      protected override global::Google.ProtocolBuffers.TestProtos.ImportMessage MessageBeingBuilt {
+      protected override ImportMessage MessageBeingBuilt {
         get { return result; }
       }
       
       public override Builder Clear() {
-        result = new global::Google.ProtocolBuffers.TestProtos.ImportMessage();
+        result = new ImportMessage();
         return this;
       }
       
@@ -179,30 +157,30 @@ namespace Google.ProtocolBuffers.TestProtos {
       }
       
       public override pbd::MessageDescriptor DescriptorForType {
-        get { return global::Google.ProtocolBuffers.TestProtos.ImportMessage.Descriptor; }
+        get { return ImportMessage.Descriptor; }
       }
       
-      public override global::Google.ProtocolBuffers.TestProtos.ImportMessage DefaultInstanceForType {
-        get { return global::Google.ProtocolBuffers.TestProtos.ImportMessage.DefaultInstance; }
+      public override ImportMessage DefaultInstanceForType {
+        get { return ImportMessage.DefaultInstance; }
       }
       
-      public override global::Google.ProtocolBuffers.TestProtos.ImportMessage BuildPartial() {
-        global::Google.ProtocolBuffers.TestProtos.ImportMessage returnMe = result;
+      public override ImportMessage BuildPartial() {
+        ImportMessage returnMe = result;
         result = null;
         return returnMe;
       }
       
       public override Builder MergeFrom(pb::IMessage other) {
-        if (other is global::Google.ProtocolBuffers.TestProtos.ImportMessage) {
-          return MergeFrom((global::Google.ProtocolBuffers.TestProtos.ImportMessage) other);
+        if (other is ImportMessage) {
+          return MergeFrom((ImportMessage) other);
         } else {
           base.MergeFrom(other);
           return this;
         }
       }
       
-      public override Builder MergeFrom(global::Google.ProtocolBuffers.TestProtos.ImportMessage other) {
-        if (other == global::Google.ProtocolBuffers.TestProtos.ImportMessage.DefaultInstance) return this;
+      public override Builder MergeFrom(ImportMessage other) {
+        if (other == ImportMessage.DefaultInstance) return this;
         if (other.HasD) {
           D = other.D;
         }
@@ -215,17 +193,16 @@ namespace Google.ProtocolBuffers.TestProtos {
       }
       
       public override Builder MergeFrom(pb::CodedInputStream input, pb::ExtensionRegistry extensionRegistry) {
-        pb::UnknownFieldSet.Builder unknownFields =
-          pb::UnknownFieldSet.CreateBuilder(this.UnknownFields);
+        pb::UnknownFieldSet.Builder unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields);
         while (true) {
           uint tag = input.ReadTag();
           switch (tag) {
-            case 0:
+            case 0: {
               this.UnknownFields = unknownFields.Build();
               return this;
+            }
             default: {
-              if (!ParseUnknownField(input, unknownFields,
-                                     extensionRegistry, tag)) {
+              if (!ParseUnknownField(input, unknownFields, extensionRegistry, tag)) {
                 this.UnknownFields = unknownFields.Build();
                 return this;
               }
@@ -240,7 +217,6 @@ namespace Google.ProtocolBuffers.TestProtos {
       }
       
       
-      // optional int32 d = 1;
       public bool HasD {
         get { return result.HasD; }
       }
@@ -263,6 +239,4 @@ namespace Google.ProtocolBuffers.TestProtos {
   
   #endregion
   
-  #region Services
-  #endregion
 }

+ 49 - 0
csharp/ProtocolBuffers/DescriptorProtos/CSharpOptions.cs

@@ -0,0 +1,49 @@
+// 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
+  }
+}

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 243 - 350
csharp/ProtocolBuffers/DescriptorProtos/DescriptorProtoFile.cs


+ 15 - 0
csharp/ProtocolBuffers/DescriptorProtos/PartialClasses.cs

@@ -18,6 +18,21 @@
 // autogenerated classes, so that they implement
 // IDescriptorProto
 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 EnumDescriptorProto : IDescriptorProto<EnumOptions> { }
   public partial class EnumValueDescriptorProto : IDescriptorProto<EnumValueOptions> { }

+ 3 - 3
csharp/ProtocolBuffers/Descriptors/FileDescriptor.cs

@@ -181,7 +181,7 @@ namespace Google.ProtocolBuffers.Descriptors {
     /// 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>
     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
       // constructor), we build an object tree mirroring the
       // FileDescriptorProto's tree and put all of the descriptors into the
@@ -204,9 +204,9 @@ namespace Google.ProtocolBuffers.Descriptors {
       }
       for (int i = 0; i < proto.DependencyCount; i++) {
         if (dependencies[i].Name != proto.DependencyList[i]) {
-          throw new DescriptorValidationException(result,
+          /*throw new DescriptorValidationException(result,
             "Dependencies passed to FileDescriptor.BuildFrom() don't match " +
-            "those listed in the FileDescriptorProto.");
+            "those listed in the FileDescriptorProto.");*/
         }
       }
 

+ 1 - 1
csharp/ProtocolBuffers/FieldAccess/RepeatedPrimitiveAccessor.cs

@@ -48,7 +48,7 @@ namespace Google.ProtocolBuffers.FieldAccess {
       PropertyInfo messageProperty = typeof(TMessage).GetProperty(name + "List");
       PropertyInfo builderProperty = typeof(TBuilder).GetProperty(name + "List");
       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) });
       clrType = getElementMethod.ReturnType;
       MethodInfo addMethod = typeof(TBuilder).GetMethod("Add" + name, new Type[] { ClrType });

+ 6 - 3
csharp/ProtocolBuffers/FieldAccess/SinglePrimitiveAccessor.cs

@@ -39,10 +39,13 @@ namespace Google.ProtocolBuffers.FieldAccess {
     }
 
     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);
-      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) {
         throw new ArgumentException("Not all required properties/methods available");
       }

+ 1 - 0
csharp/ProtocolBuffers/FieldSet.cs

@@ -33,6 +33,7 @@ namespace Google.ProtocolBuffers {
   /// be impossible to guarantee if this were a public class, of course.
   /// 
   /// All repeated fields are stored as IList[object] even 
+  /// TODO(jonskeet): Finish this comment!
   /// </summary>
   internal sealed class FieldSet {
 

+ 1 - 0
csharp/ProtocolBuffers/ProtocolBuffers.csproj

@@ -48,6 +48,7 @@
     <Compile Include="Collections\Dictionaries.cs" />
     <Compile Include="Collections\Lists.cs" />
     <Compile Include="Collections\ReadOnlyDictionary.cs" />
+    <Compile Include="DescriptorProtos\CSharpOptions.cs" />
     <Compile Include="DescriptorProtos\DescriptorProtoFile.cs" />
     <Compile Include="DescriptorProtos\IDescriptorProto.cs" />
     <Compile Include="DescriptorProtos\PartialClasses.cs" />

+ 4 - 0
csharp/ProtocolBuffers/TextGenerator.cs

@@ -97,6 +97,10 @@ namespace Google.ProtocolBuffers {
       Write(text.Substring(pos));
     }
 
+    public void Write(string format, params object[] args) {
+      Write(string.Format(format, args));
+    }
+    
     private void Write(string data) {
       if (data.Length == 0) {
         return;

+ 6 - 0
csharp/ProtocolBuffers/UnknownFieldSet.cs

@@ -18,6 +18,7 @@ using System.Collections.Generic;
 using System.IO;
 using Google.ProtocolBuffers.Collections;
 using Google.ProtocolBuffers.Descriptors;
+using Google.ProtocolBuffers.DescriptorProtos;
 
 namespace Google.ProtocolBuffers {
   /// <summary>
@@ -454,6 +455,11 @@ namespace Google.ProtocolBuffers {
       /// <returns>true unless the tag is an end-group tag</returns>
       internal bool MergeFieldFrom(CodedInputStream input, 
           ExtensionRegistry extensionRegistry, IBuilder builder, uint tag) {
+
+        if (DescriptorProtoFile.Bootstrapping) {
+          return MergeFieldFrom(tag, input);
+        }
+
         MessageDescriptor type = builder.DescriptorForType;
         if (type.Options.MessageSetWireFormat && tag == WireFormat.MessageSetTag.ItemStart) {
           MergeMessageSetExtensionFromCodedStream(input, extensionRegistry, builder);

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác