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 import java.util.Collection; 36 import java.util.Collections; 37 import java.util.HashMap; 38 import java.util.HashSet; 39 import java.util.Map; 40 import java.util.Set; 41 42 /** 43 * A table of known extensions, searchable by name or field number. When parsing a protocol message 44 * that might have extensions, you must provide an {@code ExtensionRegistry} in which you have 45 * registered any extensions that you want to be able to parse. Otherwise, those extensions will 46 * just be treated like unknown fields. 47 * 48 * <p>For example, if you had the {@code .proto} file: 49 * 50 * <pre> 51 * option java_class = "MyProto"; 52 * 53 * message Foo { 54 * extensions 1000 to max; 55 * } 56 * 57 * extend Foo { 58 * optional int32 bar; 59 * } 60 * </pre> 61 * 62 * Then you might write code like: 63 * 64 * <pre> 65 * ExtensionRegistry registry = ExtensionRegistry.newInstance(); 66 * registry.add(MyProto.bar); 67 * MyProto.Foo message = MyProto.Foo.parseFrom(input, registry); 68 * </pre> 69 * 70 * <p>Background: 71 * 72 * <p>You might wonder why this is necessary. Two alternatives might come to mind. First, you might 73 * imagine a system where generated extensions are automatically registered when their containing 74 * classes are loaded. This is a popular technique, but is bad design; among other things, it 75 * creates a situation where behavior can change depending on what classes happen to be loaded. It 76 * also introduces a security vulnerability, because an unprivileged class could cause its code to 77 * be called unexpectedly from a privileged class by registering itself as an extension of the right 78 * type. 79 * 80 * <p>Another option you might consider is lazy parsing: do not parse an extension until it is first 81 * requested, at which point the caller must provide a type to use. This introduces a different set 82 * of problems. First, it would require a mutex lock any time an extension was accessed, which would 83 * be slow. Second, corrupt data would not be detected until first access, at which point it would 84 * be much harder to deal with it. Third, it could violate the expectation that message objects are 85 * immutable, since the type provided could be any arbitrary message class. An unprivileged user 86 * could take advantage of this to inject a mutable object into a message belonging to privileged 87 * code and create mischief. 88 * 89 * @author kenton@google.com Kenton Varda 90 */ 91 public class ExtensionRegistry extends ExtensionRegistryLite { 92 /** Construct a new, empty instance. */ newInstance()93 public static ExtensionRegistry newInstance() { 94 return new ExtensionRegistry(); 95 } 96 97 /** Get the unmodifiable singleton empty instance. */ getEmptyRegistry()98 public static ExtensionRegistry getEmptyRegistry() { 99 return EMPTY_REGISTRY; 100 } 101 102 103 /** Returns an unmodifiable view of the registry. */ 104 @Override getUnmodifiable()105 public ExtensionRegistry getUnmodifiable() { 106 return new ExtensionRegistry(this); 107 } 108 109 /** A (Descriptor, Message) pair, returned by lookup methods. */ 110 public static final class ExtensionInfo { 111 /** The extension's descriptor. */ 112 public final FieldDescriptor descriptor; 113 114 /** 115 * A default instance of the extension's type, if it has a message type. Otherwise, {@code 116 * null}. 117 */ 118 public final Message defaultInstance; 119 ExtensionInfo(final FieldDescriptor descriptor)120 private ExtensionInfo(final FieldDescriptor descriptor) { 121 this.descriptor = descriptor; 122 defaultInstance = null; 123 } 124 ExtensionInfo(final FieldDescriptor descriptor, final Message defaultInstance)125 private ExtensionInfo(final FieldDescriptor descriptor, final Message defaultInstance) { 126 this.descriptor = descriptor; 127 this.defaultInstance = defaultInstance; 128 } 129 } 130 131 /** Deprecated. Use {@link #findImmutableExtensionByName(String)} instead. */ 132 @Deprecated findExtensionByName(final String fullName)133 public ExtensionInfo findExtensionByName(final String fullName) { 134 return findImmutableExtensionByName(fullName); 135 } 136 137 /** 138 * Find an extension for immutable APIs by fully-qualified field name, in the proto namespace. 139 * i.e. {@code result.descriptor.fullName()} will match {@code fullName} if a match is found. 140 * 141 * @return Information about the extension if found, or {@code null} otherwise. 142 */ findImmutableExtensionByName(final String fullName)143 public ExtensionInfo findImmutableExtensionByName(final String fullName) { 144 return immutableExtensionsByName.get(fullName); 145 } 146 147 /** 148 * Find an extension for mutable APIs by fully-qualified field name, in the proto namespace. i.e. 149 * {@code result.descriptor.fullName()} will match {@code fullName} if a match is found. 150 * 151 * @return Information about the extension if found, or {@code null} otherwise. 152 */ findMutableExtensionByName(final String fullName)153 public ExtensionInfo findMutableExtensionByName(final String fullName) { 154 return mutableExtensionsByName.get(fullName); 155 } 156 157 /** Deprecated. Use {@link #findImmutableExtensionByNumber( Descriptors.Descriptor, int)} */ 158 @Deprecated findExtensionByNumber( final Descriptor containingType, final int fieldNumber)159 public ExtensionInfo findExtensionByNumber( 160 final Descriptor containingType, final int fieldNumber) { 161 return findImmutableExtensionByNumber(containingType, fieldNumber); 162 } 163 164 /** 165 * Find an extension by containing type and field number for immutable APIs. 166 * 167 * @return Information about the extension if found, or {@code null} otherwise. 168 */ findImmutableExtensionByNumber( final Descriptor containingType, final int fieldNumber)169 public ExtensionInfo findImmutableExtensionByNumber( 170 final Descriptor containingType, final int fieldNumber) { 171 return immutableExtensionsByNumber.get(new DescriptorIntPair(containingType, fieldNumber)); 172 } 173 174 /** 175 * Find an extension by containing type and field number for mutable APIs. 176 * 177 * @return Information about the extension if found, or {@code null} otherwise. 178 */ findMutableExtensionByNumber( final Descriptor containingType, final int fieldNumber)179 public ExtensionInfo findMutableExtensionByNumber( 180 final Descriptor containingType, final int fieldNumber) { 181 return mutableExtensionsByNumber.get(new DescriptorIntPair(containingType, fieldNumber)); 182 } 183 184 /** 185 * Find all extensions for mutable APIs by fully-qualified name of extended class. Note that this 186 * method is more computationally expensive than getting a single extension by name or number. 187 * 188 * @return Information about the extensions found, or {@code null} if there are none. 189 */ getAllMutableExtensionsByExtendedType(final String fullName)190 public Set<ExtensionInfo> getAllMutableExtensionsByExtendedType(final String fullName) { 191 HashSet<ExtensionInfo> extensions = new HashSet<ExtensionInfo>(); 192 for (DescriptorIntPair pair : mutableExtensionsByNumber.keySet()) { 193 if (pair.descriptor.getFullName().equals(fullName)) { 194 extensions.add(mutableExtensionsByNumber.get(pair)); 195 } 196 } 197 return extensions; 198 } 199 200 /** 201 * Find all extensions for immutable APIs by fully-qualified name of extended class. Note that 202 * this method is more computationally expensive than getting a single extension by name or 203 * number. 204 * 205 * @return Information about the extensions found, or {@code null} if there are none. 206 */ getAllImmutableExtensionsByExtendedType(final String fullName)207 public Set<ExtensionInfo> getAllImmutableExtensionsByExtendedType(final String fullName) { 208 HashSet<ExtensionInfo> extensions = new HashSet<ExtensionInfo>(); 209 for (DescriptorIntPair pair : immutableExtensionsByNumber.keySet()) { 210 if (pair.descriptor.getFullName().equals(fullName)) { 211 extensions.add(immutableExtensionsByNumber.get(pair)); 212 } 213 } 214 return extensions; 215 } 216 217 /** Add an extension from a generated file to the registry. */ add(final Extension<?, ?> extension)218 public void add(final Extension<?, ?> extension) { 219 if (extension.getExtensionType() != Extension.ExtensionType.IMMUTABLE 220 && extension.getExtensionType() != Extension.ExtensionType.MUTABLE) { 221 // do not support other extension types. ignore 222 return; 223 } 224 add(newExtensionInfo(extension), extension.getExtensionType()); 225 } 226 227 /** Add an extension from a generated file to the registry. */ add(final GeneratedMessage.GeneratedExtension<?, ?> extension)228 public void add(final GeneratedMessage.GeneratedExtension<?, ?> extension) { 229 add((Extension<?, ?>) extension); 230 } 231 newExtensionInfo(final Extension<?, ?> extension)232 static ExtensionInfo newExtensionInfo(final Extension<?, ?> extension) { 233 if (extension.getDescriptor().getJavaType() == FieldDescriptor.JavaType.MESSAGE) { 234 if (extension.getMessageDefaultInstance() == null) { 235 throw new IllegalStateException( 236 "Registered message-type extension had null default instance: " 237 + extension.getDescriptor().getFullName()); 238 } 239 return new ExtensionInfo( 240 extension.getDescriptor(), (Message) extension.getMessageDefaultInstance()); 241 } else { 242 return new ExtensionInfo(extension.getDescriptor(), null); 243 } 244 } 245 246 /** Add a non-message-type extension to the registry by descriptor. */ add(final FieldDescriptor type)247 public void add(final FieldDescriptor type) { 248 if (type.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { 249 throw new IllegalArgumentException( 250 "ExtensionRegistry.add() must be provided a default instance when " 251 + "adding an embedded message extension."); 252 } 253 ExtensionInfo info = new ExtensionInfo(type, null); 254 add(info, Extension.ExtensionType.IMMUTABLE); 255 add(info, Extension.ExtensionType.MUTABLE); 256 } 257 258 /** Add a message-type extension to the registry by descriptor. */ add(final FieldDescriptor type, final Message defaultInstance)259 public void add(final FieldDescriptor type, final Message defaultInstance) { 260 if (type.getJavaType() != FieldDescriptor.JavaType.MESSAGE) { 261 throw new IllegalArgumentException( 262 "ExtensionRegistry.add() provided a default instance for a non-message extension."); 263 } 264 add(new ExtensionInfo(type, defaultInstance), Extension.ExtensionType.IMMUTABLE); 265 } 266 267 // ================================================================= 268 // Private stuff. 269 ExtensionRegistry()270 private ExtensionRegistry() { 271 this.immutableExtensionsByName = new HashMap<String, ExtensionInfo>(); 272 this.mutableExtensionsByName = new HashMap<String, ExtensionInfo>(); 273 this.immutableExtensionsByNumber = new HashMap<DescriptorIntPair, ExtensionInfo>(); 274 this.mutableExtensionsByNumber = new HashMap<DescriptorIntPair, ExtensionInfo>(); 275 } 276 ExtensionRegistry(ExtensionRegistry other)277 private ExtensionRegistry(ExtensionRegistry other) { 278 super(other); 279 this.immutableExtensionsByName = Collections.unmodifiableMap(other.immutableExtensionsByName); 280 this.mutableExtensionsByName = Collections.unmodifiableMap(other.mutableExtensionsByName); 281 this.immutableExtensionsByNumber = 282 Collections.unmodifiableMap(other.immutableExtensionsByNumber); 283 this.mutableExtensionsByNumber = Collections.unmodifiableMap(other.mutableExtensionsByNumber); 284 } 285 286 private final Map<String, ExtensionInfo> immutableExtensionsByName; 287 private final Map<String, ExtensionInfo> mutableExtensionsByName; 288 private final Map<DescriptorIntPair, ExtensionInfo> immutableExtensionsByNumber; 289 private final Map<DescriptorIntPair, ExtensionInfo> mutableExtensionsByNumber; 290 ExtensionRegistry(boolean empty)291 ExtensionRegistry(boolean empty) { 292 super(EMPTY_REGISTRY_LITE); 293 this.immutableExtensionsByName = Collections.<String, ExtensionInfo>emptyMap(); 294 this.mutableExtensionsByName = Collections.<String, ExtensionInfo>emptyMap(); 295 this.immutableExtensionsByNumber = Collections.<DescriptorIntPair, ExtensionInfo>emptyMap(); 296 this.mutableExtensionsByNumber = Collections.<DescriptorIntPair, ExtensionInfo>emptyMap(); 297 } 298 299 static final ExtensionRegistry EMPTY_REGISTRY = new ExtensionRegistry(true); 300 add(final ExtensionInfo extension, final Extension.ExtensionType extensionType)301 private void add(final ExtensionInfo extension, final Extension.ExtensionType extensionType) { 302 if (!extension.descriptor.isExtension()) { 303 throw new IllegalArgumentException( 304 "ExtensionRegistry.add() was given a FieldDescriptor for a regular " 305 + "(non-extension) field."); 306 } 307 308 Map<String, ExtensionInfo> extensionsByName; 309 Map<DescriptorIntPair, ExtensionInfo> extensionsByNumber; 310 switch (extensionType) { 311 case IMMUTABLE: 312 extensionsByName = immutableExtensionsByName; 313 extensionsByNumber = immutableExtensionsByNumber; 314 break; 315 case MUTABLE: 316 extensionsByName = mutableExtensionsByName; 317 extensionsByNumber = mutableExtensionsByNumber; 318 break; 319 default: 320 // Ignore the unknown supported type. 321 return; 322 } 323 324 extensionsByName.put(extension.descriptor.getFullName(), extension); 325 extensionsByNumber.put( 326 new DescriptorIntPair( 327 extension.descriptor.getContainingType(), extension.descriptor.getNumber()), 328 extension); 329 330 final FieldDescriptor field = extension.descriptor; 331 if (field.getContainingType().getOptions().getMessageSetWireFormat() 332 && field.getType() == FieldDescriptor.Type.MESSAGE 333 && field.isOptional() 334 && field.getExtensionScope() == field.getMessageType()) { 335 // This is an extension of a MessageSet type defined within the extension 336 // type's own scope. For backwards-compatibility, allow it to be looked 337 // up by type name. 338 extensionsByName.put(field.getMessageType().getFullName(), extension); 339 } 340 } 341 342 /** A (GenericDescriptor, int) pair, used as a map key. */ 343 private static final class DescriptorIntPair { 344 private final Descriptor descriptor; 345 private final int number; 346 DescriptorIntPair(final Descriptor descriptor, final int number)347 DescriptorIntPair(final Descriptor descriptor, final int number) { 348 this.descriptor = descriptor; 349 this.number = number; 350 } 351 352 @Override hashCode()353 public int hashCode() { 354 return descriptor.hashCode() * ((1 << 16) - 1) + number; 355 } 356 357 @Override equals(final Object obj)358 public boolean equals(final Object obj) { 359 if (!(obj instanceof DescriptorIntPair)) { 360 return false; 361 } 362 final DescriptorIntPair other = (DescriptorIntPair) obj; 363 return descriptor == other.descriptor && number == other.number; 364 } 365 } 366 } 367