blob: 154b690277c167d1193158708fd4836fa5f690fc [file] [log] [blame]
Jon Skeet0aac0e42009-09-09 18:48:02 +01001#region Copyright notice and license
Jon Skeet60c059b2008-10-23 21:17:56 +01002// Protocol Buffers - Google's data interchange format
3// Copyright 2008 Google Inc. All rights reserved.
4// http://github.com/jskeet/dotnet-protobufs/
5// Original C++/Java/Python code:
Jon Skeet68036862008-10-22 13:30:34 +01006// http://code.google.com/p/protobuf/
7//
Jon Skeet60c059b2008-10-23 21:17:56 +01008// Redistribution and use in source and binary forms, with or without
9// modification, are permitted provided that the following conditions are
10// met:
Jon Skeet68036862008-10-22 13:30:34 +010011//
Jon Skeet60c059b2008-10-23 21:17:56 +010012// * Redistributions of source code must retain the above copyright
13// notice, this list of conditions and the following disclaimer.
14// * Redistributions in binary form must reproduce the above
15// copyright notice, this list of conditions and the following disclaimer
16// in the documentation and/or other materials provided with the
17// distribution.
18// * Neither the name of Google Inc. nor the names of its
19// contributors may be used to endorse or promote products derived from
20// this software without specific prior written permission.
Jon Skeet68036862008-10-22 13:30:34 +010021//
Jon Skeet60c059b2008-10-23 21:17:56 +010022// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Jon Skeet0aac0e42009-09-09 18:48:02 +010033#endregion
34
Jon Skeet68036862008-10-22 13:30:34 +010035using System.Collections.Generic;
36using Google.ProtocolBuffers.Descriptors;
37using System;
38
39namespace Google.ProtocolBuffers {
40 /// <summary>
41 /// A table of known extensions, searchable by name or field number. When
42 /// parsing a protocol message that might have extensions, you must provide
43 /// an <see cref="ExtensionRegistry"/> in which you have registered any extensions
44 /// that you want to be able to parse. Otherwise, those extensions will just
45 /// be treated like unknown fields.
46 /// </summary>
47 /// <example>
48 /// For example, if you had the <c>.proto</c> file:
49 /// <code>
50 /// option java_class = "MyProto";
51 ///
52 /// message Foo {
53 /// extensions 1000 to max;
54 /// }
55 ///
56 /// extend Foo {
57 /// optional int32 bar;
58 /// }
59 /// </code>
60 ///
61 /// Then you might write code like:
62 ///
63 /// <code>
64 /// ExtensionRegistry registry = ExtensionRegistry.CreateInstance();
65 /// registry.Add(MyProto.Bar);
66 /// MyProto.Foo message = MyProto.Foo.ParseFrom(input, registry);
67 /// </code>
68 /// </example>
69 ///
70 /// <remarks>
71 /// <para>You might wonder why this is necessary. Two alternatives might come to
72 /// mind. First, you might imagine a system where generated extensions are
73 /// automatically registered when their containing classes are loaded. This
74 /// is a popular technique, but is bad design; among other things, it creates a
75 /// situation where behavior can change depending on what classes happen to be
76 /// loaded. It also introduces a security vulnerability, because an
77 /// unprivileged class could cause its code to be called unexpectedly from a
78 /// privileged class by registering itself as an extension of the right type.
79 /// </para>
80 /// <para>Another option you might consider is lazy parsing: do not parse an
81 /// extension until it is first requested, at which point the caller must
82 /// provide a type to use. This introduces a different set of problems. First,
83 /// it would require a mutex lock any time an extension was accessed, which
84 /// would be slow. Second, corrupt data would not be detected until first
85 /// access, at which point it would be much harder to deal with it. Third, it
86 /// could violate the expectation that message objects are immutable, since the
87 /// type provided could be any arbitrary message class. An unprivileged user
88 /// could take advantage of this to inject a mutable object into a message
89 /// belonging to privileged code and create mischief.</para>
90 /// </remarks>
91 public sealed class ExtensionRegistry {
92
93 private static readonly ExtensionRegistry empty = new ExtensionRegistry(
94 new Dictionary<string, ExtensionInfo>(),
95 new Dictionary<DescriptorIntPair, ExtensionInfo>(),
96 true);
97
98 private readonly IDictionary<string, ExtensionInfo> extensionsByName;
99 private readonly IDictionary<DescriptorIntPair, ExtensionInfo> extensionsByNumber;
100 private readonly bool readOnly;
101
102 private ExtensionRegistry(IDictionary<String, ExtensionInfo> extensionsByName,
103 IDictionary<DescriptorIntPair, ExtensionInfo> extensionsByNumber,
104 bool readOnly) {
105 this.extensionsByName = extensionsByName;
106 this.extensionsByNumber = extensionsByNumber;
107 this.readOnly = readOnly;
108 }
109
110 /// <summary>
111 /// Construct a new, empty instance.
112 /// </summary>
113 public static ExtensionRegistry CreateInstance() {
114 return new ExtensionRegistry(new Dictionary<string, ExtensionInfo>(),
115 new Dictionary<DescriptorIntPair, ExtensionInfo>(), false);
116 }
117
118 /// <summary>
119 /// Get the unmodifiable singleton empty instance.
120 /// </summary>
121 public static ExtensionRegistry Empty {
122 get { return empty; }
123 }
124
125 public ExtensionRegistry AsReadOnly() {
126 return new ExtensionRegistry(extensionsByName, extensionsByNumber, true);
127 }
128
129 /// <summary>
130 /// Finds an extension by fully-qualified field name, in the
131 /// proto namespace, i.e. result.Descriptor.FullName will match
132 /// <paramref name="fullName"/> if a match is found. A null
133 /// reference is returned if the extension can't be found.
134 /// </summary>
135 public ExtensionInfo this[string fullName] {
136 get {
137 ExtensionInfo ret;
138 extensionsByName.TryGetValue(fullName, out ret);
139 return ret;
140 }
141 }
142
143 /// <summary>
144 /// Finds an extension by containing type and field number.
145 /// A null reference is returned if the extension can't be found.
146 /// </summary>
147 public ExtensionInfo this[MessageDescriptor containingType, int fieldNumber] {
148 get {
149 ExtensionInfo ret;
150 extensionsByNumber.TryGetValue(new DescriptorIntPair(containingType, fieldNumber), out ret);
151 return ret;
152 }
153 }
154
155 /// <summary>
156 /// Add an extension from a generated file to the registry.
157 /// </summary>
158 public void Add<TExtension> (GeneratedExtensionBase<TExtension> extension) {
159 if (extension.Descriptor.MappedType == MappedType.Message) {
160 Add(new ExtensionInfo(extension.Descriptor, extension.MessageDefaultInstance));
161 } else {
162 Add(new ExtensionInfo(extension.Descriptor, null));
163 }
164 }
165
166 /// <summary>
167 /// Adds a non-message-type extension to the registry by descriptor.
168 /// </summary>
169 /// <param name="type"></param>
170 public void Add(FieldDescriptor type) {
171 if (type.MappedType == MappedType.Message) {
172 throw new ArgumentException("ExtensionRegistry.Add() must be provided a default instance "
173 + "when adding an embedded message extension.");
174 }
175 Add(new ExtensionInfo(type, null));
176 }
177
178 /// <summary>
179 /// Adds a message-type-extension to the registry by descriptor.
180 /// </summary>
181 /// <param name="type"></param>
182 /// <param name="defaultInstance"></param>
183 public void Add(FieldDescriptor type, IMessage defaultInstance) {
184 if (type.MappedType != MappedType.Message) {
185 throw new ArgumentException("ExtensionRegistry.Add() provided a default instance for a "
186 + "non-message extension.");
187 }
188 Add(new ExtensionInfo(type, defaultInstance));
189 }
190
191 private void Add(ExtensionInfo extension) {
192 if (readOnly) {
193 throw new InvalidOperationException("Cannot add entries to a read-only extension registry");
194 }
195 if (!extension.Descriptor.IsExtension) {
196 throw new ArgumentException("ExtensionRegistry.add() was given a FieldDescriptor for a "
197 + "regular (non-extension) field.");
198 }
199
200 extensionsByName[extension.Descriptor.FullName] = extension;
201 extensionsByNumber[new DescriptorIntPair(extension.Descriptor.ContainingType,
202 extension.Descriptor.FieldNumber)] = extension;
203
204 FieldDescriptor field = extension.Descriptor;
205 if (field.ContainingType.Options.MessageSetWireFormat
206 && field.FieldType == FieldType.Message
207 && field.IsOptional
208 && field.ExtensionScope == field.MessageType) {
209 // This is an extension of a MessageSet type defined within the extension
210 // type's own scope. For backwards-compatibility, allow it to be looked
211 // up by type name.
212 extensionsByName[field.MessageType.FullName] = extension;
213 }
214 }
215
216 /// <summary>
217 /// Nested type just used to represent a pair of MessageDescriptor and int, as
218 /// the key into the "by number" map.
219 /// </summary>
220 private struct DescriptorIntPair : IEquatable<DescriptorIntPair> {
221 readonly MessageDescriptor descriptor;
222 readonly int number;
223
224 internal DescriptorIntPair(MessageDescriptor descriptor, int number) {
225 this.descriptor = descriptor;
226 this.number = number;
227 }
228
229 public override int GetHashCode() {
230 return descriptor.GetHashCode() * ((1 << 16) - 1) + number;
231 }
232
233 public override bool Equals(object obj) {
234 if (!(obj is DescriptorIntPair)) {
235 return false;
236 }
237 return Equals((DescriptorIntPair)obj);
238 }
239
240 public bool Equals(DescriptorIntPair other) {
241 return descriptor == other.descriptor && number == other.number;
242 }
243 }
244 }
245}