1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 // https://developers.google.com/protocol-buffers/
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 //     * Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer.
11 //     * Redistributions in binary form must reproduce the above
12 // copyright notice, this list of conditions and the following disclaimer
13 // in the documentation and/or other materials provided with the
14 // distribution.
15 //     * Neither the name of Google Inc. nor the names of its
16 // contributors may be used to endorse or promote products derived from
17 // this software without specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 
31 package com.google.protobuf;
32 
33 import com.google.protobuf.Descriptors.Descriptor;
34 import com.google.protobuf.Descriptors.FieldDescriptor;
35 
36 import java.util.Collection;
37 import java.util.Collections;
38 import java.util.HashMap;
39 import java.util.HashSet;
40 import java.util.Map;
41 import java.util.Set;
42 
43 /**
44  * A table of known extensions, searchable by name or field number.  When
45  * parsing a protocol message that might have extensions, you must provide
46  * an {@code ExtensionRegistry} in which you have registered any extensions
47  * that you want to be able to parse.  Otherwise, those extensions will just
48  * be treated like unknown fields.
49  *
50  * <p>For example, if you had the {@code .proto} file:
51  *
52  * <pre>
53  * option java_class = "MyProto";
54  *
55  * message Foo {
56  *   extensions 1000 to max;
57  * }
58  *
59  * extend Foo {
60  *   optional int32 bar;
61  * }
62  * </pre>
63  *
64  * Then you might write code like:
65  *
66  * <pre>
67  * ExtensionRegistry registry = ExtensionRegistry.newInstance();
68  * registry.add(MyProto.bar);
69  * MyProto.Foo message = MyProto.Foo.parseFrom(input, registry);
70  * </pre>
71  *
72  * <p>Background:
73  *
74  * <p>You might wonder why this is necessary.  Two alternatives might come to
75  * mind.  First, you might imagine a system where generated extensions are
76  * automatically registered when their containing classes are loaded.  This
77  * is a popular technique, but is bad design; among other things, it creates a
78  * situation where behavior can change depending on what classes happen to be
79  * loaded.  It also introduces a security vulnerability, because an
80  * unprivileged class could cause its code to be called unexpectedly from a
81  * privileged class by registering itself as an extension of the right type.
82  *
83  * <p>Another option you might consider is lazy parsing: do not parse an
84  * extension until it is first requested, at which point the caller must
85  * provide a type to use.  This introduces a different set of problems.  First,
86  * it would require a mutex lock any time an extension was accessed, which
87  * would be slow.  Second, corrupt data would not be detected until first
88  * access, at which point it would be much harder to deal with it.  Third, it
89  * could violate the expectation that message objects are immutable, since the
90  * type provided could be any arbitrary message class.  An unprivileged user
91  * could take advantage of this to inject a mutable object into a message
92  * belonging to privileged code and create mischief.
93  *
94  * @author kenton@google.com Kenton Varda
95  */
96 public class ExtensionRegistry extends ExtensionRegistryLite {
97   /** Construct a new, empty instance. */
newInstance()98   public static ExtensionRegistry newInstance() {
99     return new ExtensionRegistry();
100   }
101 
102   /** Get the unmodifiable singleton empty instance. */
getEmptyRegistry()103   public static ExtensionRegistry getEmptyRegistry() {
104     return EMPTY_REGISTRY;
105   }
106 
107 
108   /** Returns an unmodifiable view of the registry. */
109   @Override
getUnmodifiable()110   public ExtensionRegistry getUnmodifiable() {
111     return new ExtensionRegistry(this);
112   }
113 
114   /** A (Descriptor, Message) pair, returned by lookup methods. */
115   public static final class ExtensionInfo {
116     /** The extension's descriptor. */
117     public final FieldDescriptor descriptor;
118 
119     /**
120      * A default instance of the extension's type, if it has a message type.
121      * Otherwise, {@code null}.
122      */
123     public final Message defaultInstance;
124 
ExtensionInfo(final FieldDescriptor descriptor)125     private ExtensionInfo(final FieldDescriptor descriptor) {
126       this.descriptor = descriptor;
127       defaultInstance = null;
128     }
ExtensionInfo(final FieldDescriptor descriptor, final Message defaultInstance)129     private ExtensionInfo(final FieldDescriptor descriptor,
130                           final Message defaultInstance) {
131       this.descriptor = descriptor;
132       this.defaultInstance = defaultInstance;
133     }
134   }
135 
136   /**
137    * Deprecated. Use {@link #findImmutableExtensionByName(String)} instead.
138    */
findExtensionByName(final String fullName)139   public ExtensionInfo findExtensionByName(final String fullName) {
140     return findImmutableExtensionByName(fullName);
141   }
142 
143   /**
144    * Find an extension for immutable APIs by fully-qualified field name,
145    * in the proto namespace. i.e. {@code result.descriptor.fullName()} will
146    * match {@code fullName} if a match is found.
147    *
148    * @return Information about the extension if found, or {@code null}
149    *         otherwise.
150    */
findImmutableExtensionByName(final String fullName)151   public ExtensionInfo findImmutableExtensionByName(final String fullName) {
152     return immutableExtensionsByName.get(fullName);
153   }
154 
155   /**
156    * Find an extension for mutable APIs by fully-qualified field name,
157    * in the proto namespace. i.e. {@code result.descriptor.fullName()} will
158    * match {@code fullName} if a match is found.
159    *
160    * @return Information about the extension if found, or {@code null}
161    *         otherwise.
162    */
findMutableExtensionByName(final String fullName)163   public ExtensionInfo findMutableExtensionByName(final String fullName) {
164     return mutableExtensionsByName.get(fullName);
165   }
166 
167   /**
168    * Deprecated. Use {@link #findImmutableExtensionByNumber(
169    * Descriptors.Descriptor, int)}
170    */
findExtensionByNumber( final Descriptor containingType, final int fieldNumber)171   public ExtensionInfo findExtensionByNumber(
172       final Descriptor containingType, final int fieldNumber) {
173     return findImmutableExtensionByNumber(containingType, fieldNumber);
174   }
175 
176   /**
177    * Find an extension by containing type and field number for immutable APIs.
178    *
179    * @return Information about the extension if found, or {@code null}
180    *         otherwise.
181    */
findImmutableExtensionByNumber( final Descriptor containingType, final int fieldNumber)182   public ExtensionInfo findImmutableExtensionByNumber(
183       final Descriptor containingType, final int fieldNumber) {
184     return immutableExtensionsByNumber.get(
185       new DescriptorIntPair(containingType, fieldNumber));
186   }
187 
188   /**
189    * Find an extension by containing type and field number for mutable APIs.
190    *
191    * @return Information about the extension if found, or {@code null}
192    *         otherwise.
193    */
findMutableExtensionByNumber( final Descriptor containingType, final int fieldNumber)194   public ExtensionInfo findMutableExtensionByNumber(
195       final Descriptor containingType, final int fieldNumber) {
196     return mutableExtensionsByNumber.get(
197         new DescriptorIntPair(containingType, fieldNumber));
198   }
199 
200   /**
201    * Find all extensions for mutable APIs by fully-qualified name of
202    * extended class. Note that this method is more computationally expensive
203    * than getting a single extension by name or number.
204    *
205    * @return Information about the extensions found, or {@code null} if there
206    *     are none.
207    */
getAllMutableExtensionsByExtendedType(final String fullName)208   public Set<ExtensionInfo> getAllMutableExtensionsByExtendedType(final String fullName) {
209     HashSet<ExtensionInfo> extensions = new HashSet<ExtensionInfo>();
210     for (DescriptorIntPair pair : mutableExtensionsByNumber.keySet()) {
211       if (pair.descriptor.getFullName().equals(fullName)) {
212         extensions.add(mutableExtensionsByNumber.get(pair));
213       }
214     }
215     return extensions;
216   }
217 
218   /**
219    * Find all extensions for immutable APIs by fully-qualified name of
220    * extended class. Note that this method is more computationally expensive
221    * than getting a single extension by name or number.
222    *
223    * @return Information about the extensions found, or {@code null} if there
224    *     are none.
225    */
getAllImmutableExtensionsByExtendedType(final String fullName)226   public Set<ExtensionInfo> getAllImmutableExtensionsByExtendedType(final String fullName) {
227     HashSet<ExtensionInfo> extensions = new HashSet<ExtensionInfo>();
228     for (DescriptorIntPair pair : immutableExtensionsByNumber.keySet()) {
229       if (pair.descriptor.getFullName().equals(fullName)) {
230         extensions.add(immutableExtensionsByNumber.get(pair));
231       }
232     }
233     return extensions;
234   }
235 
236   /** Add an extension from a generated file to the registry. */
add(final Extension<?, ?> extension)237   public void add(final Extension<?, ?> extension) {
238     if (extension.getExtensionType() != Extension.ExtensionType.IMMUTABLE &&
239         extension.getExtensionType() != Extension.ExtensionType.MUTABLE) {
240       // do not support other extension types. ignore
241       return;
242     }
243     add(newExtensionInfo(extension), extension.getExtensionType());
244   }
245 
246   /** Add an extension from a generated file to the registry. */
add(final GeneratedMessage.GeneratedExtension<?, ?> extension)247   public void add(final GeneratedMessage.GeneratedExtension<?, ?> extension) {
248     add((Extension<?, ?>) extension);
249   }
250 
newExtensionInfo(final Extension<?, ?> extension)251   static ExtensionInfo newExtensionInfo(final Extension<?, ?> extension) {
252     if (extension.getDescriptor().getJavaType() ==
253         FieldDescriptor.JavaType.MESSAGE) {
254       if (extension.getMessageDefaultInstance() == null) {
255         throw new IllegalStateException(
256             "Registered message-type extension had null default instance: " +
257                 extension.getDescriptor().getFullName());
258       }
259       return new ExtensionInfo(extension.getDescriptor(),
260           (Message) extension.getMessageDefaultInstance());
261     } else {
262       return new ExtensionInfo(extension.getDescriptor(), null);
263     }
264   }
265 
266   /** Add a non-message-type extension to the registry by descriptor. */
add(final FieldDescriptor type)267   public void add(final FieldDescriptor type) {
268     if (type.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
269       throw new IllegalArgumentException(
270         "ExtensionRegistry.add() must be provided a default instance when " +
271         "adding an embedded message extension.");
272     }
273     ExtensionInfo info = new ExtensionInfo(type, null);
274     add(info, Extension.ExtensionType.IMMUTABLE);
275     add(info, Extension.ExtensionType.MUTABLE);
276   }
277 
278   /** Add a message-type extension to the registry by descriptor. */
add(final FieldDescriptor type, final Message defaultInstance)279   public void add(final FieldDescriptor type, final Message defaultInstance) {
280     if (type.getJavaType() != FieldDescriptor.JavaType.MESSAGE) {
281       throw new IllegalArgumentException(
282         "ExtensionRegistry.add() provided a default instance for a " +
283         "non-message extension.");
284     }
285       add(new ExtensionInfo(type, defaultInstance),
286           Extension.ExtensionType.IMMUTABLE);
287   }
288 
289   // =================================================================
290   // Private stuff.
291 
ExtensionRegistry()292   private ExtensionRegistry() {
293     this.immutableExtensionsByName = new HashMap<String, ExtensionInfo>();
294     this.mutableExtensionsByName = new HashMap<String, ExtensionInfo>();
295     this.immutableExtensionsByNumber =
296         new HashMap<DescriptorIntPair, ExtensionInfo>();
297     this.mutableExtensionsByNumber =
298         new HashMap<DescriptorIntPair, ExtensionInfo>();
299   }
300 
ExtensionRegistry(ExtensionRegistry other)301   private ExtensionRegistry(ExtensionRegistry other) {
302     super(other);
303     this.immutableExtensionsByName =
304         Collections.unmodifiableMap(other.immutableExtensionsByName);
305     this.mutableExtensionsByName =
306         Collections.unmodifiableMap(other.mutableExtensionsByName);
307     this.immutableExtensionsByNumber =
308         Collections.unmodifiableMap(other.immutableExtensionsByNumber);
309     this.mutableExtensionsByNumber =
310             Collections.unmodifiableMap(other.mutableExtensionsByNumber);
311   }
312 
313   private final Map<String, ExtensionInfo> immutableExtensionsByName;
314   private final Map<String, ExtensionInfo> mutableExtensionsByName;
315   private final Map<DescriptorIntPair, ExtensionInfo> immutableExtensionsByNumber;
316   private final Map<DescriptorIntPair, ExtensionInfo> mutableExtensionsByNumber;
317 
ExtensionRegistry(boolean empty)318   ExtensionRegistry(boolean empty) {
319     super(EMPTY_REGISTRY_LITE);
320     this.immutableExtensionsByName =
321         Collections.<String, ExtensionInfo>emptyMap();
322     this.mutableExtensionsByName =
323         Collections.<String, ExtensionInfo>emptyMap();
324     this.immutableExtensionsByNumber =
325         Collections.<DescriptorIntPair, ExtensionInfo>emptyMap();
326     this.mutableExtensionsByNumber =
327             Collections.<DescriptorIntPair, ExtensionInfo>emptyMap();
328   }
329   static final ExtensionRegistry EMPTY_REGISTRY = new ExtensionRegistry(true);
330 
add( final ExtensionInfo extension, final Extension.ExtensionType extensionType)331   private void add(
332       final ExtensionInfo extension,
333       final Extension.ExtensionType extensionType) {
334     if (!extension.descriptor.isExtension()) {
335       throw new IllegalArgumentException(
336           "ExtensionRegistry.add() was given a FieldDescriptor for a regular " +
337               "(non-extension) field.");
338     }
339 
340     Map<String, ExtensionInfo> extensionsByName;
341     Map<DescriptorIntPair, ExtensionInfo> extensionsByNumber;
342     switch (extensionType) {
343       case IMMUTABLE:
344         extensionsByName = immutableExtensionsByName;
345         extensionsByNumber = immutableExtensionsByNumber;
346         break;
347       case MUTABLE:
348         extensionsByName = mutableExtensionsByName;
349         extensionsByNumber = mutableExtensionsByNumber;
350         break;
351       default:
352         // Ignore the unknown supported type.
353         return;
354     }
355 
356     extensionsByName.put(extension.descriptor.getFullName(), extension);
357     extensionsByNumber.put(
358       new DescriptorIntPair(extension.descriptor.getContainingType(),
359                             extension.descriptor.getNumber()),
360       extension);
361 
362     final FieldDescriptor field = extension.descriptor;
363     if (field.getContainingType().getOptions().getMessageSetWireFormat() &&
364         field.getType() == FieldDescriptor.Type.MESSAGE &&
365         field.isOptional() &&
366         field.getExtensionScope() == field.getMessageType()) {
367       // This is an extension of a MessageSet type defined within the extension
368       // type's own scope.  For backwards-compatibility, allow it to be looked
369       // up by type name.
370       extensionsByName.put(field.getMessageType().getFullName(), extension);
371     }
372   }
373 
374   /** A (GenericDescriptor, int) pair, used as a map key. */
375   private static final class DescriptorIntPair {
376     private final Descriptor descriptor;
377     private final int number;
378 
DescriptorIntPair(final Descriptor descriptor, final int number)379     DescriptorIntPair(final Descriptor descriptor, final int number) {
380       this.descriptor = descriptor;
381       this.number = number;
382     }
383 
384     @Override
hashCode()385     public int hashCode() {
386       return descriptor.hashCode() * ((1 << 16) - 1) + number;
387     }
388     @Override
equals(final Object obj)389     public boolean equals(final Object obj) {
390       if (!(obj instanceof DescriptorIntPair)) {
391         return false;
392       }
393       final DescriptorIntPair other = (DescriptorIntPair)obj;
394       return descriptor == other.descriptor && number == other.number;
395     }
396   }
397 }
398