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