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