1 #region Copyright notice and license 2 // Protocol Buffers - Google's data interchange format 3 // Copyright 2015 Google Inc. All rights reserved. 4 // https://developers.google.com/protocol-buffers/ 5 // 6 // Redistribution and use in source and binary forms, with or without 7 // modification, are permitted provided that the following conditions are 8 // met: 9 // 10 // * Redistributions of source code must retain the above copyright 11 // notice, this list of conditions and the following disclaimer. 12 // * Redistributions in binary form must reproduce the above 13 // copyright notice, this list of conditions and the following disclaimer 14 // in the documentation and/or other materials provided with the 15 // distribution. 16 // * Neither the name of Google Inc. nor the names of its 17 // contributors may be used to endorse or promote products derived from 18 // this software without specific prior written permission. 19 // 20 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 #endregion 32 using System.Collections.Generic; 33 using System.Linq; 34 35 namespace Google.Protobuf.Reflection 36 { 37 /// <summary> 38 /// An immutable registry of types which can be looked up by their full name. 39 /// </summary> 40 public sealed class TypeRegistry 41 { 42 /// <summary> 43 /// An empty type registry, containing no types. 44 /// </summary> 45 public static TypeRegistry Empty { get; } = new TypeRegistry(new Dictionary<string, MessageDescriptor>()); 46 47 private readonly Dictionary<string, MessageDescriptor> fullNameToMessageMap; 48 TypeRegistry(Dictionary<string, MessageDescriptor> fullNameToMessageMap)49 private TypeRegistry(Dictionary<string, MessageDescriptor> fullNameToMessageMap) 50 { 51 this.fullNameToMessageMap = fullNameToMessageMap; 52 } 53 54 /// <summary> 55 /// Attempts to find a message descriptor by its full name. 56 /// </summary> 57 /// <param name="fullName">The full name of the message, which is the dot-separated 58 /// combination of package, containing messages and message name</param> 59 /// <returns>The message descriptor corresponding to <paramref name="fullName"/> or null 60 /// if there is no such message descriptor.</returns> Find(string fullName)61 public MessageDescriptor Find(string fullName) 62 { 63 MessageDescriptor ret; 64 // Ignore the return value as ret will end up with the right value either way. 65 fullNameToMessageMap.TryGetValue(fullName, out ret); 66 return ret; 67 } 68 69 /// <summary> 70 /// Creates a type registry from the specified set of file descriptors. 71 /// </summary> 72 /// <remarks> 73 /// This is a convenience overload for <see cref="FromFiles(IEnumerable{FileDescriptor})"/> 74 /// to allow calls such as <c>TypeRegistry.FromFiles(descriptor1, descriptor2)</c>. 75 /// </remarks> 76 /// <param name="fileDescriptors">The set of files to include in the registry. Must not contain null values.</param> 77 /// <returns>A type registry for the given files.</returns> FromFiles(params FileDescriptor[] fileDescriptors)78 public static TypeRegistry FromFiles(params FileDescriptor[] fileDescriptors) 79 { 80 return FromFiles((IEnumerable<FileDescriptor>) fileDescriptors); 81 } 82 83 /// <summary> 84 /// Creates a type registry from the specified set of file descriptors. 85 /// </summary> 86 /// <remarks> 87 /// All message types within all the specified files are added to the registry, and 88 /// the dependencies of the specified files are also added, recursively. 89 /// </remarks> 90 /// <param name="fileDescriptors">The set of files to include in the registry. Must not contain null values.</param> 91 /// <returns>A type registry for the given files.</returns> FromFiles(IEnumerable<FileDescriptor> fileDescriptors)92 public static TypeRegistry FromFiles(IEnumerable<FileDescriptor> fileDescriptors) 93 { 94 ProtoPreconditions.CheckNotNull(fileDescriptors, nameof(fileDescriptors)); 95 var builder = new Builder(); 96 foreach (var file in fileDescriptors) 97 { 98 builder.AddFile(file); 99 } 100 return builder.Build(); 101 } 102 103 /// <summary> 104 /// Creates a type registry from the file descriptor parents of the specified set of message descriptors. 105 /// </summary> 106 /// <remarks> 107 /// This is a convenience overload for <see cref="FromMessages(IEnumerable{MessageDescriptor})"/> 108 /// to allow calls such as <c>TypeRegistry.FromFiles(descriptor1, descriptor2)</c>. 109 /// </remarks> 110 /// <param name="messageDescriptors">The set of message descriptors to use to identify file descriptors to include in the registry. 111 /// Must not contain null values.</param> 112 /// <returns>A type registry for the given files.</returns> FromMessages(params MessageDescriptor[] messageDescriptors)113 public static TypeRegistry FromMessages(params MessageDescriptor[] messageDescriptors) 114 { 115 return FromMessages((IEnumerable<MessageDescriptor>) messageDescriptors); 116 } 117 118 /// <summary> 119 /// Creates a type registry from the file descriptor parents of the specified set of message descriptors. 120 /// </summary> 121 /// <remarks> 122 /// The specified message descriptors are only used to identify their file descriptors; the returned registry 123 /// contains all the types within the file descriptors which contain the specified message descriptors (and 124 /// the dependencies of those files), not just the specified messages. 125 /// </remarks> 126 /// <param name="messageDescriptors">The set of message descriptors to use to identify file descriptors to include in the registry. 127 /// Must not contain null values.</param> 128 /// <returns>A type registry for the given files.</returns> FromMessages(IEnumerable<MessageDescriptor> messageDescriptors)129 public static TypeRegistry FromMessages(IEnumerable<MessageDescriptor> messageDescriptors) 130 { 131 ProtoPreconditions.CheckNotNull(messageDescriptors, nameof(messageDescriptors)); 132 return FromFiles(messageDescriptors.Select(md => md.File)); 133 } 134 135 /// <summary> 136 /// Builder class which isn't exposed, but acts as a convenient alternative to passing round two dictionaries in recursive calls. 137 /// </summary> 138 private class Builder 139 { 140 private readonly Dictionary<string, MessageDescriptor> types; 141 private readonly HashSet<string> fileDescriptorNames; 142 Builder()143 internal Builder() 144 { 145 types = new Dictionary<string, MessageDescriptor>(); 146 fileDescriptorNames = new HashSet<string>(); 147 } 148 AddFile(FileDescriptor fileDescriptor)149 internal void AddFile(FileDescriptor fileDescriptor) 150 { 151 if (!fileDescriptorNames.Add(fileDescriptor.Name)) 152 { 153 return; 154 } 155 foreach (var dependency in fileDescriptor.Dependencies) 156 { 157 AddFile(dependency); 158 } 159 foreach (var message in fileDescriptor.MessageTypes) 160 { 161 AddMessage(message); 162 } 163 } 164 AddMessage(MessageDescriptor messageDescriptor)165 private void AddMessage(MessageDescriptor messageDescriptor) 166 { 167 foreach (var nestedType in messageDescriptor.NestedTypes) 168 { 169 AddMessage(nestedType); 170 } 171 // This will overwrite any previous entry. Given that each file should 172 // only be added once, this could be a problem such as package A.B with type C, 173 // and package A with type B.C... it's unclear what we should do in that case. 174 types[messageDescriptor.FullName] = messageDescriptor; 175 } 176 Build()177 internal TypeRegistry Build() 178 { 179 return new TypeRegistry(types); 180 } 181 } 182 } 183 } 184