aboutsummaryrefslogtreecommitdiffhomepage
path: root/csharp/src/ProtocolBuffers/Descriptors/FileDescriptor.cs
diff options
context:
space:
mode:
authorGravatar Jon Skeet <jonskeet@google.com>2015-04-28 14:49:49 +0100
committerGravatar Jon Skeet <jonskeet@google.com>2015-04-28 14:53:24 +0100
commitf3504cf3b1d456a843e8242fdee9ba0bf2991dc1 (patch)
tree019441e4649f66f6273a13bcff5dbafbd12fc9b6 /csharp/src/ProtocolBuffers/Descriptors/FileDescriptor.cs
parentb977c3ed63146525064463c33ddfaa96152989ed (diff)
First part of making the C# runtime work with the new codegen.
1) Remove CSharpOptions 2) A new version of DescriptorProtoFile (with manual changes from codegen - it would otherwise be Descriptor.cs) 3) Turn off CLS compliance (which we'll remove from the codebase entirely; I don't think it's actually relevant these days) 4) Add "public imports" to FileDescriptor, with code broadly copied from the Java codebase. Lots more changes to commit before it will build and tests run, but one step at a time...
Diffstat (limited to 'csharp/src/ProtocolBuffers/Descriptors/FileDescriptor.cs')
-rw-r--r--csharp/src/ProtocolBuffers/Descriptors/FileDescriptor.cs193
1 files changed, 80 insertions, 113 deletions
diff --git a/csharp/src/ProtocolBuffers/Descriptors/FileDescriptor.cs b/csharp/src/ProtocolBuffers/Descriptors/FileDescriptor.cs
index d7075e35..354e99a3 100644
--- a/csharp/src/ProtocolBuffers/Descriptors/FileDescriptor.cs
+++ b/csharp/src/ProtocolBuffers/Descriptors/FileDescriptor.cs
@@ -51,16 +51,17 @@ namespace Google.ProtocolBuffers.Descriptors
private readonly IList<ServiceDescriptor> services;
private readonly IList<FieldDescriptor> extensions;
private readonly IList<FileDescriptor> dependencies;
+ private readonly IList<FileDescriptor> publicDependencies;
private readonly DescriptorPool pool;
- private CSharpFileOptions csharpFileOptions;
- private readonly object optionsLock = new object();
- private FileDescriptor(FileDescriptorProto proto, FileDescriptor[] dependencies, DescriptorPool pool)
+ private FileDescriptor(FileDescriptorProto proto, FileDescriptor[] dependencies, DescriptorPool pool, bool allowUnknownDependencies)
{
this.pool = pool;
this.proto = proto;
this.dependencies = new ReadOnlyCollection<FileDescriptor>((FileDescriptor[]) dependencies.Clone());
+ publicDependencies = DeterminePublicDependencies(this, proto, dependencies, allowUnknownDependencies);
+
pool.AddPackage(Package, this);
messageTypes = DescriptorUtil.ConvertAndMakeReadOnly(proto.MessageTypeList,
@@ -80,94 +81,46 @@ namespace Google.ProtocolBuffers.Descriptors
new FieldDescriptor(field, this, null, index, true));
}
-
/// <summary>
- /// Allows a file descriptor to be configured with a set of external options, e.g. from the
- /// command-line arguments to protogen.
+ /// Extracts public dependencies from direct dependencies. This is a static method despite its
+ /// first parameter, as the value we're in the middle of constructing is only used for exceptions.
/// </summary>
- public void ConfigureWithDefaultOptions(CSharpFileOptions options)
- {
- csharpFileOptions = BuildOrFakeWithDefaultOptions(options);
- }
-
- static readonly char[] PathSeperators = new char[] { '/', '\\' };
- private CSharpFileOptions BuildOrFakeWithDefaultOptions(CSharpFileOptions defaultOptions)
+ private static IList<FileDescriptor> DeterminePublicDependencies(FileDescriptor @this, FileDescriptorProto proto, FileDescriptor[] dependencies, bool allowUnknownDependencies)
{
- // Fix for being able to relocate these files to any directory structure
- if (proto.Package == "google.protobuf")
- {
- int ixslash = proto.Name.LastIndexOfAny(PathSeperators);
- string filename = ixslash < 0 ? proto.Name : proto.Name.Substring(ixslash + 1);
- // TODO(jonskeet): Check if we could use FileDescriptorProto.Descriptor.Name - interesting bootstrap issues)
- if (filename == "descriptor.proto")
- {
- return new CSharpFileOptions.Builder
- {
- Namespace = "Google.ProtocolBuffers.DescriptorProtos",
- UmbrellaClassname = "DescriptorProtoFile",
- NestClasses = false,
- MultipleFiles = false,
- PublicClasses = true,
- OutputDirectory = defaultOptions.OutputDirectory,
- IgnoreGoogleProtobuf = defaultOptions.IgnoreGoogleProtobuf
- }.Build();
- }
- if (filename == "csharp_options.proto")
- {
- return new CSharpFileOptions.Builder
- {
- Namespace = "Google.ProtocolBuffers.DescriptorProtos",
- UmbrellaClassname = "CSharpOptions",
- NestClasses = false,
- MultipleFiles = false,
- PublicClasses = true,
- OutputDirectory = defaultOptions.OutputDirectory,
- IgnoreGoogleProtobuf = defaultOptions.IgnoreGoogleProtobuf
- }.Build();
- }
- }
- CSharpFileOptions.Builder builder = defaultOptions.ToBuilder();
- if (proto.Options.HasExtension(DescriptorProtos.CSharpOptions.CSharpFileOptions))
- {
- builder.MergeFrom(proto.Options.GetExtension(DescriptorProtos.CSharpOptions.CSharpFileOptions));
- }
- if (!builder.HasNamespace)
- {
- builder.Namespace = Package;
- }
- if (!builder.HasUmbrellaClassname)
+ var nameToFileMap = new Dictionary<string, FileDescriptor>();
+ foreach (var file in dependencies)
{
- int lastSlash = Name.LastIndexOf('/');
- string baseName = Name.Substring(lastSlash + 1);
- builder.UmbrellaClassname = NameHelpers.UnderscoresToPascalCase(NameHelpers.StripProto(baseName));
+ nameToFileMap[file.Name] = file;
}
-
- // Auto-fix for name collision by placing umbrella class into a new namespace. This
- // still won't fix the collisions with nesting enabled; however, you have to turn that on explicitly anyway.
- if (!builder.NestClasses && !builder.HasUmbrellaNamespace)
+ var publicDependencies = new List<FileDescriptor>();
+ for (int i = 0; i < proto.PublicDependencyCount; i++)
{
- bool collision = false;
- foreach (IDescriptor d in MessageTypes)
- {
- collision |= d.Name == builder.UmbrellaClassname;
- }
- foreach (IDescriptor d in Services)
+ int index = proto.PublicDependencyList[i];
+ if (index < 0 || index >= proto.DependencyCount)
{
- collision |= d.Name == builder.UmbrellaClassname;
+ throw new DescriptorValidationException(@this, "Invalid public dependency index.");
}
- foreach (IDescriptor d in EnumTypes)
+ string name = proto.DependencyList[index];
+ FileDescriptor file = nameToFileMap[name];
+ if (file == null)
{
- collision |= d.Name == builder.UmbrellaClassname;
+ if (!allowUnknownDependencies)
+ {
+ throw new DescriptorValidationException(@this, "Invalid public dependency: " + name);
+ }
+ // Ignore unknown dependencies.
}
- if (collision)
+ else
{
- builder.UmbrellaNamespace = "Proto";
+ publicDependencies.Add(file);
}
}
-
- return builder.Build();
+ return new ReadOnlyCollection<FileDescriptor>(publicDependencies);
}
+
+ static readonly char[] PathSeperators = new char[] { '/', '\\' };
+
/// <value>
/// The descriptor in its protocol message representation.
/// </value>
@@ -184,25 +137,6 @@ namespace Google.ProtocolBuffers.Descriptors
get { return proto.Options; }
}
- /// <summary>
- /// Returns the C#-specific options for this file descriptor. This will always be
- /// completely filled in.
- /// </summary>
- public CSharpFileOptions CSharpOptions
- {
- get
- {
- lock (optionsLock)
- {
- if (csharpFileOptions == null)
- {
- csharpFileOptions = BuildOrFakeWithDefaultOptions(CSharpFileOptions.DefaultInstance);
- }
- }
- return csharpFileOptions;
- }
- }
-
/// <value>
/// The file name.
/// </value>
@@ -261,6 +195,14 @@ namespace Google.ProtocolBuffers.Descriptors
}
/// <value>
+ /// Unmodifiable list of this file's public dependencies (public imports).
+ /// </value>
+ public IList<FileDescriptor> PublicDependencies
+ {
+ get { return publicDependencies; }
+ }
+
+ /// <value>
/// Implementation of IDescriptor.FullName - just returns the same as Name.
/// </value>
string IDescriptor.FullName
@@ -331,6 +273,22 @@ namespace Google.ProtocolBuffers.Descriptors
/// having an undefined type or because two messages were defined with the same name.</exception>
public static FileDescriptor BuildFrom(FileDescriptorProto proto, FileDescriptor[] dependencies)
{
+ return BuildFrom(proto, dependencies, false);
+ }
+
+ /// <summary>
+ /// Builds a FileDescriptor from its protocol buffer representation.
+ /// </summary>
+ /// <param name="proto">The protocol message form of the FileDescriptor.</param>
+ /// <param name="dependencies">FileDescriptors corresponding to all of the
+ /// file's dependencies, in the exact order listed in the .proto file. May be null,
+ /// in which case it is treated as an empty array.</param>
+ /// <param name="allowUnknownDependencies">Whether unknown dependencies are ignored (true) or cause an exception to be thrown (false).</param>
+ /// <exception cref="DescriptorValidationException">If <paramref name="proto"/> is not
+ /// 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>
+ private static FileDescriptor BuildFrom(FileDescriptorProto proto, FileDescriptor[] dependencies, bool allowUnknownDependencies)
+ {
// Building descriptors involves two steps: translating and linking.
// In the translation step (implemented by FileDescriptor's
// constructor), we build an object tree mirroring the
@@ -346,23 +304,25 @@ namespace Google.ProtocolBuffers.Descriptors
}
DescriptorPool pool = new DescriptorPool(dependencies);
- FileDescriptor result = new FileDescriptor(proto, dependencies, pool);
-
- if (dependencies.Length != proto.DependencyCount)
- {
- throw new DescriptorValidationException(result,
- "Dependencies passed to FileDescriptor.BuildFrom() don't match " +
- "those listed in the FileDescriptorProto.");
- }
- for (int i = 0; i < proto.DependencyCount; i++)
- {
- if (dependencies[i].Name != proto.DependencyList[i])
- {
- throw new DescriptorValidationException(result,
- "Dependencies passed to FileDescriptor.BuildFrom() don't match " +
- "those listed in the FileDescriptorProto.");
- }
- }
+ FileDescriptor result = new FileDescriptor(proto, dependencies, pool, allowUnknownDependencies);
+
+ // TODO(jonskeet): Reinstate these checks, or get rid of them entirely. They aren't in the Java code,
+ // and fail for the CustomOptions test right now. (We get "descriptor.proto" vs "google/protobuf/descriptor.proto".)
+ //if (dependencies.Length != proto.DependencyCount)
+ //{
+ // throw new DescriptorValidationException(result,
+ // "Dependencies passed to FileDescriptor.BuildFrom() don't match " +
+ // "those listed in the FileDescriptorProto.");
+ //}
+ //for (int i = 0; i < proto.DependencyCount; i++)
+ //{
+ // if (dependencies[i].Name != proto.DependencyList[i])
+ // {
+ // throw new DescriptorValidationException(result,
+ // "Dependencies passed to FileDescriptor.BuildFrom() don't match " +
+ // "those listed in the FileDescriptorProto.");
+ // }
+ //}
result.CrossLink();
return result;
@@ -434,7 +394,9 @@ namespace Google.ProtocolBuffers.Descriptors
FileDescriptor result;
try
{
- result = BuildFrom(proto, dependencies);
+ // When building descriptors for generated code, we allow unknown
+ // dependencies by default.
+ result = BuildFrom(proto, dependencies, true);
}
catch (DescriptorValidationException e)
{
@@ -494,5 +456,10 @@ namespace Google.ProtocolBuffers.Descriptors
extensions[i].ReplaceProto(proto.GetExtension(i));
}
}
+
+ public override string ToString()
+ {
+ return "FileDescriptor for " + proto.Name;
+ }
}
} \ No newline at end of file