Переглянути джерело

Final commit before changing layout

Jon Skeet 17 роки тому
батько
коміт
e60ce8bfaf
39 змінених файлів з 2406 додано та 444 видалено
  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
+  }
+}

Різницю між файлами не показано, бо вона завелика
+ 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);

Деякі файли не було показано, через те що забагато файлів було змінено