aboutsummaryrefslogtreecommitdiff
path: root/src/ProtoGen/Generator.cs
blob: f656386ba95b754a25af0bb37e3b8afc642be557 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
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>
  /// Code generator for protocol buffers. Only C# is supported at the moment.
  /// </summary>
  public sealed class Generator {

    readonly GeneratorOptions options;

    private Generator(GeneratorOptions options) {
      options.Validate();
      this.options = options;
    }

    /// <summary>
    /// Returns a generator configured with the specified options.
    /// </summary>
    public static Generator CreateGenerator(GeneratorOptions options) {
      return new Generator(options);
    }

    public void Generate() {
      foreach (string inputFile in options.InputFiles) {
        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, extensionRegistry);
        }
        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 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);
    }
  }
}