aboutsummaryrefslogtreecommitdiff
path: root/csharp/src/ProtocolBuffers/Descriptors
diff options
context:
space:
mode:
Diffstat (limited to 'csharp/src/ProtocolBuffers/Descriptors')
-rw-r--r--csharp/src/ProtocolBuffers/Descriptors/DescriptorPool.cs22
-rw-r--r--csharp/src/ProtocolBuffers/Descriptors/FieldDescriptor.cs54
-rw-r--r--csharp/src/ProtocolBuffers/Descriptors/FileDescriptor.cs193
-rw-r--r--csharp/src/ProtocolBuffers/Descriptors/MessageDescriptor.cs19
4 files changed, 98 insertions, 190 deletions
diff --git a/csharp/src/ProtocolBuffers/Descriptors/DescriptorPool.cs b/csharp/src/ProtocolBuffers/Descriptors/DescriptorPool.cs
index ae819801..30718709 100644
--- a/csharp/src/ProtocolBuffers/Descriptors/DescriptorPool.cs
+++ b/csharp/src/ProtocolBuffers/Descriptors/DescriptorPool.cs
@@ -50,14 +50,15 @@ namespace Google.ProtocolBuffers.Descriptors
private readonly IDictionary<DescriptorIntPair, EnumValueDescriptor> enumValuesByNumber =
new Dictionary<DescriptorIntPair, EnumValueDescriptor>();
- private readonly DescriptorPool[] dependencies;
+ private readonly HashSet<FileDescriptor> dependencies;
internal DescriptorPool(FileDescriptor[] dependencyFiles)
{
- dependencies = new DescriptorPool[dependencyFiles.Length];
+ dependencies = new HashSet<FileDescriptor>();
for (int i = 0; i < dependencyFiles.Length; i++)
{
- dependencies[i] = dependencyFiles[i].DescriptorPool;
+ dependencies.Add(dependencyFiles[i]);
+ ImportPublicDependencies(dependencyFiles[i]);
}
foreach (FileDescriptor dependency in dependencyFiles)
@@ -66,6 +67,17 @@ namespace Google.ProtocolBuffers.Descriptors
}
}
+ private void ImportPublicDependencies(FileDescriptor file)
+ {
+ foreach (FileDescriptor dependency in file.PublicDependencies)
+ {
+ if (dependencies.Add(dependency))
+ {
+ ImportPublicDependencies(dependency);
+ }
+ }
+ }
+
/// <summary>
/// Finds a symbol of the given name within the pool.
/// </summary>
@@ -83,9 +95,9 @@ namespace Google.ProtocolBuffers.Descriptors
return descriptor;
}
- foreach (DescriptorPool dependency in dependencies)
+ foreach (FileDescriptor dependency in dependencies)
{
- dependency.descriptorsByName.TryGetValue(fullName, out result);
+ dependency.DescriptorPool.descriptorsByName.TryGetValue(fullName, out result);
descriptor = result as T;
if (descriptor != null)
{
diff --git a/csharp/src/ProtocolBuffers/Descriptors/FieldDescriptor.cs b/csharp/src/ProtocolBuffers/Descriptors/FieldDescriptor.cs
index 6d17ae2a..98de5435 100644
--- a/csharp/src/ProtocolBuffers/Descriptors/FieldDescriptor.cs
+++ b/csharp/src/ProtocolBuffers/Descriptors/FieldDescriptor.cs
@@ -51,7 +51,6 @@ namespace Google.ProtocolBuffers.Descriptors
private FieldType fieldType;
private MappedType mappedType;
- private CSharpFieldOptions csharpFieldOptions;
private readonly object optionsLock = new object();
internal FieldDescriptor(FieldDescriptorProto proto, FileDescriptor file,
@@ -101,38 +100,6 @@ namespace Google.ProtocolBuffers.Descriptors
file.DescriptorPool.AddSymbol(this);
}
- private CSharpFieldOptions BuildOrFakeCSharpOptions()
- {
- // TODO(jonskeet): Check if we could use FileDescriptorProto.Descriptor.Name - interesting bootstrap issues
- if (File.Proto.Name == "google/protobuf/csharp_options.proto")
- {
- if (Name == "csharp_field_options")
- {
- return new CSharpFieldOptions.Builder {PropertyName = "CSharpFieldOptions"}.Build();
- }
- if (Name == "csharp_file_options")
- {
- return new CSharpFieldOptions.Builder {PropertyName = "CSharpFileOptions"}.Build();
- }
- }
- CSharpFieldOptions.Builder builder = CSharpFieldOptions.CreateBuilder();
- if (Proto.Options.HasExtension(DescriptorProtos.CSharpOptions.CSharpFieldOptions))
- {
- builder.MergeFrom(Proto.Options.GetExtension(DescriptorProtos.CSharpOptions.CSharpFieldOptions));
- }
- if (!builder.HasPropertyName)
- {
- string fieldName = FieldType == FieldType.Group ? MessageType.Name : Name;
- string propertyName = NameHelpers.UnderscoresToPascalCase(fieldName);
- if (propertyName == ContainingType.Name)
- {
- propertyName += "_";
- }
- builder.PropertyName = propertyName;
- }
- return builder.Build();
- }
-
/// <summary>
/// Maps a field type as included in the .proto file to a FieldType.
/// </summary>
@@ -286,26 +253,7 @@ namespace Google.ProtocolBuffers.Descriptors
{
get { return containingType; }
}
-
- /// <summary>
- /// Returns the C#-specific options for this field descriptor. This will always be
- /// completely filled in.
- /// </summary>
- public CSharpFieldOptions CSharpOptions
- {
- get
- {
- lock (optionsLock)
- {
- if (csharpFieldOptions == null)
- {
- csharpFieldOptions = BuildOrFakeCSharpOptions();
- }
- }
- return csharpFieldOptions;
- }
- }
-
+
/// <summary>
/// For extensions defined nested within message types, gets
/// the outer type. Not valid for non-extension fields.
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
diff --git a/csharp/src/ProtocolBuffers/Descriptors/MessageDescriptor.cs b/csharp/src/ProtocolBuffers/Descriptors/MessageDescriptor.cs
index d438c0ff..5b29849c 100644
--- a/csharp/src/ProtocolBuffers/Descriptors/MessageDescriptor.cs
+++ b/csharp/src/ProtocolBuffers/Descriptors/MessageDescriptor.cs
@@ -159,25 +159,6 @@ namespace Google.ProtocolBuffers.Descriptors
}
/// <summary>
- /// Finds a field by its property name, as it would be generated by protogen.
- /// </summary>
- /// <param name="propertyName">The property name within this message type.</param>
- /// <returns>The field's descriptor, or null if not found.</returns>
- public FieldDescriptor FindFieldByPropertyName(string propertyName)
- {
- // For reasonably short messages, this will be more efficient than a dictionary
- // lookup. It also means we don't need to do things lazily with locks etc.
- foreach (FieldDescriptor field in Fields)
- {
- if (field.CSharpOptions.PropertyName == propertyName)
- {
- return field;
- }
- }
- return null;
- }
-
- /// <summary>
/// Finds a nested descriptor by name. The is valid for fields, nested
/// message types and enums.
/// </summary>