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