diff options
author | Jon Skeet <skeet@pobox.com> | 2015-11-22 16:24:27 +0000 |
---|---|---|
committer | Jon Skeet <skeet@pobox.com> | 2015-11-22 16:24:27 +0000 |
commit | d6202a9b8948d5a2d5436e3b35b175ed9b8a9fd1 (patch) | |
tree | a8a6a6da178f3581d626471c1fff758922f7a898 /csharp/src/Google.Protobuf | |
parent | 956a770adc772184e5911f964b866028465d1727 (diff) | |
parent | f7e43c6f80a29530242f64b5a13790aac5679db7 (diff) | |
download | protobuf-d6202a9b8948d5a2d5436e3b35b175ed9b8a9fd1.tar.gz protobuf-d6202a9b8948d5a2d5436e3b35b175ed9b8a9fd1.tar.bz2 protobuf-d6202a9b8948d5a2d5436e3b35b175ed9b8a9fd1.zip |
Merge pull request #994 from jskeet/type-registry
Added the type registry in advance of implementing Any support.
Diffstat (limited to 'csharp/src/Google.Protobuf')
-rw-r--r-- | csharp/src/Google.Protobuf/Google.Protobuf.csproj | 1 | ||||
-rw-r--r-- | csharp/src/Google.Protobuf/Reflection/TypeRegistry.cs | 183 |
2 files changed, 184 insertions, 0 deletions
diff --git a/csharp/src/Google.Protobuf/Google.Protobuf.csproj b/csharp/src/Google.Protobuf/Google.Protobuf.csproj index 9179ab17..36807b82 100644 --- a/csharp/src/Google.Protobuf/Google.Protobuf.csproj +++ b/csharp/src/Google.Protobuf/Google.Protobuf.csproj @@ -121,6 +121,7 @@ <Compile Include="Reflection\ServiceDescriptor.cs" />
<Compile Include="Reflection\SingleFieldAccessor.cs" />
<Compile Include="Preconditions.cs" />
+ <Compile Include="Reflection\TypeRegistry.cs" />
<Compile Include="WellKnownTypes\Any.cs" />
<Compile Include="WellKnownTypes\AnyPartial.cs" />
<Compile Include="WellKnownTypes\Api.cs" />
diff --git a/csharp/src/Google.Protobuf/Reflection/TypeRegistry.cs b/csharp/src/Google.Protobuf/Reflection/TypeRegistry.cs new file mode 100644 index 00000000..31d5a30f --- /dev/null +++ b/csharp/src/Google.Protobuf/Reflection/TypeRegistry.cs @@ -0,0 +1,183 @@ +#region Copyright notice and license +// Protocol Buffers - Google's data interchange format +// Copyright 2015 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#endregion +using System.Collections.Generic; +using System.Linq; + +namespace Google.Protobuf.Reflection +{ + /// <summary> + /// An immutable registry of types which can be looked up by their full name. + /// </summary> + public sealed class TypeRegistry + { + /// <summary> + /// An empty type registry, containing no types. + /// </summary> + public static TypeRegistry Empty { get; } = new TypeRegistry(new Dictionary<string, MessageDescriptor>()); + + private readonly Dictionary<string, MessageDescriptor> fullNameToMessageMap; + + private TypeRegistry(Dictionary<string, MessageDescriptor> fullNameToMessageMap) + { + this.fullNameToMessageMap = fullNameToMessageMap; + } + + /// <summary> + /// Attempts to find a message descriptor by its full name. + /// </summary> + /// <param name="fullName">The full name of the message, which is the dot-separated + /// combination of package, containing messages and message name</param> + /// <returns>The message descriptor corresponding to <paramref name="fullName"/> or null + /// if there is no such message descriptor.</returns> + public MessageDescriptor Find(string fullName) + { + MessageDescriptor ret; + // Ignore the return value as ret will end up with the right value either way. + fullNameToMessageMap.TryGetValue(fullName, out ret); + return ret; + } + + /// <summary> + /// Creates a type registry from the specified set of file descriptors. + /// </summary> + /// <remarks> + /// This is a convenience overload for <see cref="FromFiles(IEnumerable{FileDescriptor})"/> + /// to allow calls such as <c>TypeRegistry.FromFiles(descriptor1, descriptor2)</c>. + /// </remarks> + /// <param name="fileDescriptors">The set of files to include in the registry. Must not contain null values.</param> + /// <returns>A type registry for the given files.</returns> + public static TypeRegistry FromFiles(params FileDescriptor[] fileDescriptors) + { + return FromFiles((IEnumerable<FileDescriptor>) fileDescriptors); + } + + /// <summary> + /// Creates a type registry from the specified set of file descriptors. + /// </summary> + /// <remarks> + /// All message types within all the specified files are added to the registry, and + /// the dependencies of the specified files are also added, recursively. + /// </remarks> + /// <param name="fileDescriptors">The set of files to include in the registry. Must not contain null values.</param> + /// <returns>A type registry for the given files.</returns> + public static TypeRegistry FromFiles(IEnumerable<FileDescriptor> fileDescriptors) + { + Preconditions.CheckNotNull(fileDescriptors, nameof(fileDescriptors)); + var builder = new Builder(); + foreach (var file in fileDescriptors) + { + builder.AddFile(file); + } + return builder.Build(); + } + + /// <summary> + /// Creates a type registry from the file descriptor parents of the specified set of message descriptors. + /// </summary> + /// <remarks> + /// This is a convenience overload for <see cref="FromMessages(IEnumerable{MessageDescriptor})"/> + /// to allow calls such as <c>TypeRegistry.FromFiles(descriptor1, descriptor2)</c>. + /// </remarks> + /// <param name="messageDescriptors">The set of message descriptors to use to identify file descriptors to include in the registry. + /// Must not contain null values.</param> + /// <returns>A type registry for the given files.</returns> + public static TypeRegistry FromMessages(params MessageDescriptor[] messageDescriptors) + { + return FromMessages((IEnumerable<MessageDescriptor>) messageDescriptors); + } + + /// <summary> + /// Creates a type registry from the file descriptor parents of the specified set of message descriptors. + /// </summary> + /// <remarks> + /// The specified message descriptors are only used to identify their file descriptors; the returned registry + /// contains all the types within the file descriptors which contain the specified message descriptors (and + /// the dependencies of those files), not just the specified messages. + /// </remarks> + /// <param name="messageDescriptors">The set of message descriptors to use to identify file descriptors to include in the registry. + /// Must not contain null values.</param> + /// <returns>A type registry for the given files.</returns> + public static TypeRegistry FromMessages(IEnumerable<MessageDescriptor> messageDescriptors) + { + Preconditions.CheckNotNull(messageDescriptors, nameof(messageDescriptors)); + return FromFiles(messageDescriptors.Select(md => md.File)); + } + + /// <summary> + /// Builder class which isn't exposed, but acts as a convenient alternative to passing round two dictionaries in recursive calls. + /// </summary> + private class Builder + { + private readonly Dictionary<string, MessageDescriptor> types; + private readonly HashSet<string> fileDescriptorNames; + + internal Builder() + { + types = new Dictionary<string, MessageDescriptor>(); + fileDescriptorNames = new HashSet<string>(); + } + + internal void AddFile(FileDescriptor fileDescriptor) + { + if (!fileDescriptorNames.Add(fileDescriptor.Name)) + { + return; + } + foreach (var dependency in fileDescriptor.Dependencies) + { + AddFile(dependency); + } + foreach (var message in fileDescriptor.MessageTypes) + { + AddMessage(message); + } + } + + private void AddMessage(MessageDescriptor messageDescriptor) + { + foreach (var nestedType in messageDescriptor.NestedTypes) + { + AddMessage(nestedType); + } + // This will overwrite any previous entry. Given that each file should + // only be added once, this could be a problem such as package A.B with type C, + // and package A with type B.C... it's unclear what we should do in that case. + types[messageDescriptor.FullName] = messageDescriptor; + } + + internal TypeRegistry Build() + { + return new TypeRegistry(types); + } + } + } +} |