aboutsummaryrefslogtreecommitdiff
path: root/src/ProtoGen/Generator.cs
blob: 613136e90a6442d19abc638dce17e50e09570dec (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
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.CSharpFileOptions);
        extensionRegistry.Add(CSharpOptions.CSharpFieldOptions);
        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) {
      UmbrellaClassGenerator ucg = new UmbrellaClassGenerator(descriptor);
      using (TextWriter textWriter = File.CreateText(Path.Combine(options.OutputDirectory, descriptor.CSharpOptions.UmbrellaClassname + ".cs"))) {
        TextGenerator writer = new TextGenerator(textWriter);        
        ucg.Generate(writer);
      }
    }

    /// <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);
    }
  }
}