• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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;
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 
newExtensionInfo(final Extension<?, ?> extension)246   static ExtensionInfo newExtensionInfo(final Extension<?, ?> extension) {
247     if (extension.getDescriptor().getJavaType() ==
248         FieldDescriptor.JavaType.MESSAGE) {
249       if (extension.getMessageDefaultInstance() == null) {
250         throw new IllegalStateException(
251             "Registered message-type extension had null default instance: " +
252                 extension.getDescriptor().getFullName());
253       }
254       return new ExtensionInfo(extension.getDescriptor(),
255           (Message) extension.getMessageDefaultInstance());
256     } else {
257       return new ExtensionInfo(extension.getDescriptor(), null);
258     }
259   }
260 
261   /** Add a non-message-type extension to the registry by descriptor. */
add(final FieldDescriptor type)262   public void add(final FieldDescriptor type) {
263     if (type.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
264       throw new IllegalArgumentException(
265         "ExtensionRegistry.add() must be provided a default instance when " +
266         "adding an embedded message extension.");
267     }
268     ExtensionInfo info = new ExtensionInfo(type, null);
269     add(info, Extension.ExtensionType.IMMUTABLE);
270     add(info, Extension.ExtensionType.MUTABLE);
271   }
272 
273   /** Add a message-type extension to the registry by descriptor. */
add(final FieldDescriptor type, final Message defaultInstance)274   public void add(final FieldDescriptor type, final Message defaultInstance) {
275     if (type.getJavaType() != FieldDescriptor.JavaType.MESSAGE) {
276       throw new IllegalArgumentException(
277         "ExtensionRegistry.add() provided a default instance for a " +
278         "non-message extension.");
279     }
280       add(new ExtensionInfo(type, defaultInstance),
281           Extension.ExtensionType.IMMUTABLE);
282   }
283 
284   // =================================================================
285   // Private stuff.
286 
ExtensionRegistry()287   private ExtensionRegistry() {
288     this.immutableExtensionsByName = new HashMap<String, ExtensionInfo>();
289     this.mutableExtensionsByName = new HashMap<String, ExtensionInfo>();
290     this.immutableExtensionsByNumber =
291         new HashMap<DescriptorIntPair, ExtensionInfo>();
292     this.mutableExtensionsByNumber =
293         new HashMap<DescriptorIntPair, ExtensionInfo>();
294   }
295 
ExtensionRegistry(ExtensionRegistry other)296   private ExtensionRegistry(ExtensionRegistry other) {
297     super(other);
298     this.immutableExtensionsByName =
299         Collections.unmodifiableMap(other.immutableExtensionsByName);
300     this.mutableExtensionsByName =
301         Collections.unmodifiableMap(other.mutableExtensionsByName);
302     this.immutableExtensionsByNumber =
303         Collections.unmodifiableMap(other.immutableExtensionsByNumber);
304     this.mutableExtensionsByNumber =
305             Collections.unmodifiableMap(other.mutableExtensionsByNumber);
306   }
307 
308   private final Map<String, ExtensionInfo> immutableExtensionsByName;
309   private final Map<String, ExtensionInfo> mutableExtensionsByName;
310   private final Map<DescriptorIntPair, ExtensionInfo> immutableExtensionsByNumber;
311   private final Map<DescriptorIntPair, ExtensionInfo> mutableExtensionsByNumber;
312 
ExtensionRegistry(boolean empty)313   ExtensionRegistry(boolean empty) {
314     super(ExtensionRegistryLite.getEmptyRegistry());
315     this.immutableExtensionsByName =
316         Collections.<String, ExtensionInfo>emptyMap();
317     this.mutableExtensionsByName =
318         Collections.<String, ExtensionInfo>emptyMap();
319     this.immutableExtensionsByNumber =
320         Collections.<DescriptorIntPair, ExtensionInfo>emptyMap();
321     this.mutableExtensionsByNumber =
322             Collections.<DescriptorIntPair, ExtensionInfo>emptyMap();
323   }
324   private static final ExtensionRegistry EMPTY = new ExtensionRegistry(true);
325 
add( final ExtensionInfo extension, final Extension.ExtensionType extensionType)326   private void add(
327       final ExtensionInfo extension,
328       final Extension.ExtensionType extensionType) {
329     if (!extension.descriptor.isExtension()) {
330       throw new IllegalArgumentException(
331           "ExtensionRegistry.add() was given a FieldDescriptor for a regular " +
332               "(non-extension) field.");
333     }
334 
335     Map<String, ExtensionInfo> extensionsByName;
336     Map<DescriptorIntPair, ExtensionInfo> extensionsByNumber;
337     switch (extensionType) {
338       case IMMUTABLE:
339         extensionsByName = immutableExtensionsByName;
340         extensionsByNumber = immutableExtensionsByNumber;
341         break;
342       case MUTABLE:
343         extensionsByName = mutableExtensionsByName;
344         extensionsByNumber = mutableExtensionsByNumber;
345         break;
346       default:
347         // Ignore the unknown supported type.
348         return;
349     }
350 
351     extensionsByName.put(extension.descriptor.getFullName(), extension);
352     extensionsByNumber.put(
353       new DescriptorIntPair(extension.descriptor.getContainingType(),
354                             extension.descriptor.getNumber()),
355       extension);
356 
357     final FieldDescriptor field = extension.descriptor;
358     if (field.getContainingType().getOptions().getMessageSetWireFormat() &&
359         field.getType() == FieldDescriptor.Type.MESSAGE &&
360         field.isOptional() &&
361         field.getExtensionScope() == field.getMessageType()) {
362       // This is an extension of a MessageSet type defined within the extension
363       // type's own scope.  For backwards-compatibility, allow it to be looked
364       // up by type name.
365       extensionsByName.put(field.getMessageType().getFullName(), extension);
366     }
367   }
368 
369   /** A (GenericDescriptor, int) pair, used as a map key. */
370   private static final class DescriptorIntPair {
371     private final Descriptor descriptor;
372     private final int number;
373 
DescriptorIntPair(final Descriptor descriptor, final int number)374     DescriptorIntPair(final Descriptor descriptor, final int number) {
375       this.descriptor = descriptor;
376       this.number = number;
377     }
378 
379     @Override
hashCode()380     public int hashCode() {
381       return descriptor.hashCode() * ((1 << 16) - 1) + number;
382     }
383     @Override
equals(final Object obj)384     public boolean equals(final Object obj) {
385       if (!(obj instanceof DescriptorIntPair)) {
386         return false;
387       }
388       final DescriptorIntPair other = (DescriptorIntPair)obj;
389       return descriptor == other.descriptor && number == other.number;
390     }
391   }
392 }
393