diff options
Diffstat (limited to 'csharp/ProtocolBuffers/Descriptors/FileDescriptor.cs')
-rw-r--r-- | csharp/ProtocolBuffers/Descriptors/FileDescriptor.cs | 159 |
1 files changed, 153 insertions, 6 deletions
diff --git a/csharp/ProtocolBuffers/Descriptors/FileDescriptor.cs b/csharp/ProtocolBuffers/Descriptors/FileDescriptor.cs index d552d2f0..9e065c86 100644 --- a/csharp/ProtocolBuffers/Descriptors/FileDescriptor.cs +++ b/csharp/ProtocolBuffers/Descriptors/FileDescriptor.cs @@ -1,17 +1,64 @@ using System; +using System.Collections.ObjectModel; using Google.ProtocolBuffers.DescriptorProtos; using System.Collections.Generic; +using Google.ProtocolBuffers.Collections; namespace Google.ProtocolBuffers.Descriptors { - public class FileDescriptor : DescriptorBase<FileDescriptorProto, FileOptions> { + /// <summary> + /// Describes a .proto file, including everything defined within. + /// IDescriptor is implemented such that the File property returns this descriptor, + /// and the FullName is the same as the Name. + /// </summary> + public class FileDescriptor : IDescriptor<FileDescriptorProto> { + + private readonly FileDescriptorProto proto; private readonly IList<MessageDescriptor> messageTypes; private readonly IList<EnumDescriptor> enumTypes; private readonly IList<ServiceDescriptor> services; private readonly IList<FieldDescriptor> extensions; private readonly IList<FileDescriptor> dependencies; private readonly DescriptorPool pool; + + private FileDescriptor(FileDescriptorProto proto, FileDescriptor[] dependencies, DescriptorPool pool) { + this.pool = pool; + this.proto = proto; + this.dependencies = new ReadOnlyCollection<FileDescriptor>((FileDescriptor[]) dependencies.Clone()); + + pool.AddPackage(Package, this); + + messageTypes = DescriptorUtil.ConvertAndMakeReadOnly(proto.MessageTypeList, + (message, index) => new MessageDescriptor(message, this, null, index)); + + enumTypes = DescriptorUtil.ConvertAndMakeReadOnly(proto.EnumTypeList, + (enumType, index) => new EnumDescriptor(enumType, this, null, index)); + + services = DescriptorUtil.ConvertAndMakeReadOnly(proto.ServiceList, + (service, index) => new ServiceDescriptor(service, this, index)); + + extensions = DescriptorUtil.ConvertAndMakeReadOnly(proto.ExtensionList, + (field, index) => new FieldDescriptor(field, this, null, index, true)); + } + + /// <value> + /// The descriptor in its protocol message representation. + /// </value> + public FileDescriptorProto Proto { + get { return proto; } + } - public FileDescriptor(FileDescriptorProto proto, FileDescriptor file) : base(proto, file) { + /// <value> + /// The <see cref="FileOptions" /> defined in <c>descriptor.proto</c>. + /// </value> + public FileOptions Options { + get { return proto.Options; } + } + + /// <value> + /// The file name. + /// </value> + public string Name { + get { return proto.Name; } } /// <summary> @@ -19,7 +66,7 @@ namespace Google.ProtocolBuffers.Descriptors { /// be equivalent to the .NET namespace of the generated classes. /// </summary> public string Package { - get { return Proto.Package; } + get { return proto.Package; } } /// <value> @@ -57,10 +104,110 @@ namespace Google.ProtocolBuffers.Descriptors { get { return dependencies; } } - public static FileDescriptor BuildFrom(FileDescriptorProto proto, - FileDescriptor[] dependencies) { - throw new NotImplementedException(); + /// <value> + /// Implementation of IDescriptor.FullName - just returns the same as Name. + /// </value> + string IDescriptor.FullName { + get { return Name; } + } + + /// <value> + /// Implementation of IDescriptor.File - just returns this descriptor. + /// </value> + FileDescriptor IDescriptor.File { + get { return this; } + } + + /// <value> + /// Protocol buffer describing this descriptor. + /// </value> + IMessage IDescriptor.Proto { + get { return Proto; } + } + + /// <value> + /// Pool containing symbol descriptors. + /// </value> + internal DescriptorPool DescriptorPool { + get { return pool; } + } + + /// <summary> + /// Finds a type (message, enum, service or extension) in the file by name. Does not find nested types. + /// </summary> + /// <param name="name">The unqualified type name to look for.</param> + /// <typeparam name="T">The type of descriptor to look for (or ITypeDescriptor for any)</typeparam> + /// <returns>The type's descriptor, or null if not found.</returns> + public T FindTypeByName<T>(String name) + where T : class, IDescriptor { + // Don't allow looking up nested types. This will make optimization + // easier later. + if (name.IndexOf('.') != -1) { + return null; + } + if (Package.Length > 0) { + name = Package + "." + name; + } + T result = pool.FindSymbol<T>(name); + if (result != null && result.File == this) { + return result; + } + return null; + } + + /// <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</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> + public static FileDescriptor BuildFrom(FileDescriptorProto proto, FileDescriptor[] dependencies) { + // Building decsriptors involves two steps: translating and linking. + // In the translation step (implemented by FileDescriptor's + // constructor), we build an object tree mirroring the + // FileDescriptorProto's tree and put all of the descriptors into the + // DescriptorPool's lookup tables. In the linking step, we look up all + // type references in the DescriptorPool, so that, for example, a + // FieldDescriptor for an embedded message contains a pointer directly + // to the Descriptor for that message's type. We also detect undefined + // types in the linking step. + 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."); + } + } + + result.CrossLink(); + return result; + } + + private void CrossLink() { + foreach (MessageDescriptor message in messageTypes) { + message.CrossLink(); + } + + foreach (ServiceDescriptor service in services) { + service.CrossLink(); + } + + foreach (FieldDescriptor extension in extensions) { + extension.CrossLink(); + } } + /// <summary> /// This method is to be called by generated code only. It is equivalent /// to BuilderFrom except that the FileDescriptorProto is encoded in |