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 {
///
/// Code generator for protocol buffers. Only C# is supported at the moment.
///
public sealed class Generator {
readonly GeneratorOptions options;
private Generator(GeneratorOptions options) {
options.Validate();
this.options = options;
}
///
/// Returns a generator configured with the specified options.
///
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 descriptors = ConvertDescriptors(descriptorProtos);
foreach (FileDescriptor descriptor in descriptors) {
Generate(descriptor);
}
}
}
///
/// Generates code for a particular file. All dependencies must
/// already have been resolved.
///
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(SourceFileGenerator parentSourceGenerator, FileDescriptor file, IEnumerable siblings)
where T : IDescriptor {
}
///
/// 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.
///
/// Not all dependencies could be resolved.
internal static IList 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 fileList = descriptorProtos.FileList;
FileDescriptor[] converted = new FileDescriptor[fileList.Count];
Dictionary convertedMap = new Dictionary();
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);
}
}
}