1 // Protocol Buffers - Google's data interchange format 2 // Copyright 2008 Google Inc. All rights reserved. 3 // http://code.google.com/p/protobuf/ 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.Collections; 37 import java.util.HashMap; 38 import java.util.Map; 39 40 /** 41 * A table of known extensions, searchable by name or field number. When 42 * parsing a protocol message that might have extensions, you must provide 43 * an {@code ExtensionRegistry} in which you have registered any extensions 44 * that you want to be able to parse. Otherwise, those extensions will just 45 * be treated like unknown fields. 46 * 47 * <p>For example, if you had the {@code .proto} file: 48 * 49 * <pre> 50 * option java_class = "MyProto"; 51 * 52 * message Foo { 53 * extensions 1000 to max; 54 * } 55 * 56 * extend Foo { 57 * optional int32 bar; 58 * } 59 * </pre> 60 * 61 * Then you might write code like: 62 * 63 * <pre> 64 * ExtensionRegistry registry = ExtensionRegistry.newInstance(); 65 * registry.add(MyProto.bar); 66 * MyProto.Foo message = MyProto.Foo.parseFrom(input, registry); 67 * </pre> 68 * 69 * <p>Background: 70 * 71 * <p>You might wonder why this is necessary. Two alternatives might come to 72 * mind. First, you might imagine a system where generated extensions are 73 * automatically registered when their containing classes are loaded. This 74 * is a popular technique, but is bad design; among other things, it creates a 75 * situation where behavior can change depending on what classes happen to be 76 * loaded. It also introduces a security vulnerability, because an 77 * unprivileged class could cause its code to be called unexpectedly from a 78 * privileged class by registering itself as an extension of the right type. 79 * 80 * <p>Another option you might consider is lazy parsing: do not parse an 81 * extension until it is first requested, at which point the caller must 82 * provide a type to use. This introduces a different set of problems. First, 83 * it would require a mutex lock any time an extension was accessed, which 84 * would be slow. Second, corrupt data would not be detected until first 85 * access, at which point it would be much harder to deal with it. Third, it 86 * could violate the expectation that message objects are immutable, since the 87 * type provided could be any arbitrary message class. An unprivileged user 88 * could take advantage of this to inject a mutable object into a message 89 * belonging to privileged code and create mischief. 90 * 91 * @author kenton (at) google.com Kenton Varda 92 */ 93 public final class ExtensionRegistry extends ExtensionRegistryLite { 94 /** Construct a new, empty instance. */ 95 public static ExtensionRegistry newInstance() { 96 return new ExtensionRegistry(); 97 } 98 99 /** Get the unmodifiable singleton empty instance. */ 100 public static ExtensionRegistry getEmptyRegistry() { 101 return EMPTY; 102 } 103 104 /** Returns an unmodifiable view of the registry. */ 105 @Override 106 public ExtensionRegistry getUnmodifiable() { 107 return new ExtensionRegistry(this); 108 } 109 110 /** A (Descriptor, Message) pair, returned by lookup methods. */ 111 public static final class ExtensionInfo { 112 /** The extension's descriptor. */ 113 public final FieldDescriptor descriptor; 114 115 /** 116 * A default instance of the extension's type, if it has a message type. 117 * Otherwise, {@code null}. 118 */ 119 public final Message defaultInstance; 120 121 private ExtensionInfo(final FieldDescriptor descriptor) { 122 this.descriptor = descriptor; 123 defaultInstance = null; 124 } 125 private ExtensionInfo(final FieldDescriptor descriptor, 126 final Message defaultInstance) { 127 this.descriptor = descriptor; 128 this.defaultInstance = defaultInstance; 129 } 130 } 131 132 /** 133 * Find an extension by fully-qualified field name, in the proto namespace. 134 * I.e. {@code result.descriptor.fullName()} will match {@code fullName} if 135 * a match is found. 136 * 137 * @return Information about the extension if found, or {@code null} 138 * otherwise. 139 */ 140 public ExtensionInfo findExtensionByName(final String fullName) { 141 return extensionsByName.get(fullName); 142 } 143 144 /** 145 * Find an extension by containing type and field number. 146 * 147 * @return Information about the extension if found, or {@code null} 148 * otherwise. 149 */ 150 public ExtensionInfo findExtensionByNumber(final Descriptor containingType, 151 final int fieldNumber) { 152 return extensionsByNumber.get( 153 new DescriptorIntPair(containingType, fieldNumber)); 154 } 155 156 /** Add an extension from a generated file to the registry. */ 157 public void add(final GeneratedMessage.GeneratedExtension<?, ?> extension) { 158 if (extension.getDescriptor().getJavaType() == 159 FieldDescriptor.JavaType.MESSAGE) { 160 if (extension.getMessageDefaultInstance() == null) { 161 throw new IllegalStateException( 162 "Registered message-type extension had null default instance: " + 163 extension.getDescriptor().getFullName()); 164 } 165 add(new ExtensionInfo(extension.getDescriptor(), 166 extension.getMessageDefaultInstance())); 167 } else { 168 add(new ExtensionInfo(extension.getDescriptor(), null)); 169 } 170 } 171 172 /** Add a non-message-type extension to the registry by descriptor. */ 173 public void add(final FieldDescriptor type) { 174 if (type.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { 175 throw new IllegalArgumentException( 176 "ExtensionRegistry.add() must be provided a default instance when " + 177 "adding an embedded message extension."); 178 } 179 add(new ExtensionInfo(type, null)); 180 } 181 182 /** Add a message-type extension to the registry by descriptor. */ 183 public void add(final FieldDescriptor type, final Message defaultInstance) { 184 if (type.getJavaType() != FieldDescriptor.JavaType.MESSAGE) { 185 throw new IllegalArgumentException( 186 "ExtensionRegistry.add() provided a default instance for a " + 187 "non-message extension."); 188 } 189 add(new ExtensionInfo(type, defaultInstance)); 190 } 191 192 // ================================================================= 193 // Private stuff. 194 195 private ExtensionRegistry() { 196 this.extensionsByName = new HashMap<String, ExtensionInfo>(); 197 this.extensionsByNumber = new HashMap<DescriptorIntPair, ExtensionInfo>(); 198 } 199 200 private ExtensionRegistry(ExtensionRegistry other) { 201 super(other); 202 this.extensionsByName = Collections.unmodifiableMap(other.extensionsByName); 203 this.extensionsByNumber = 204 Collections.unmodifiableMap(other.extensionsByNumber); 205 } 206 207 private final Map<String, ExtensionInfo> extensionsByName; 208 private final Map<DescriptorIntPair, ExtensionInfo> extensionsByNumber; 209 210 private ExtensionRegistry(boolean empty) { 211 super(ExtensionRegistryLite.getEmptyRegistry()); 212 this.extensionsByName = Collections.<String, ExtensionInfo>emptyMap(); 213 this.extensionsByNumber = 214 Collections.<DescriptorIntPair, ExtensionInfo>emptyMap(); 215 } 216 private static final ExtensionRegistry EMPTY = new ExtensionRegistry(true); 217 218 private void add(final ExtensionInfo extension) { 219 if (!extension.descriptor.isExtension()) { 220 throw new IllegalArgumentException( 221 "ExtensionRegistry.add() was given a FieldDescriptor for a regular " + 222 "(non-extension) field."); 223 } 224 225 extensionsByName.put(extension.descriptor.getFullName(), extension); 226 extensionsByNumber.put( 227 new DescriptorIntPair(extension.descriptor.getContainingType(), 228 extension.descriptor.getNumber()), 229 extension); 230 231 final FieldDescriptor field = extension.descriptor; 232 if (field.getContainingType().getOptions().getMessageSetWireFormat() && 233 field.getType() == FieldDescriptor.Type.MESSAGE && 234 field.isOptional() && 235 field.getExtensionScope() == field.getMessageType()) { 236 // This is an extension of a MessageSet type defined within the extension 237 // type's own scope. For backwards-compatibility, allow it to be looked 238 // up by type name. 239 extensionsByName.put(field.getMessageType().getFullName(), extension); 240 } 241 } 242 243 /** A (GenericDescriptor, int) pair, used as a map key. */ 244 private static final class DescriptorIntPair { 245 private final Descriptor descriptor; 246 private final int number; 247 248 DescriptorIntPair(final Descriptor descriptor, final int number) { 249 this.descriptor = descriptor; 250 this.number = number; 251 } 252 253 @Override 254 public int hashCode() { 255 return descriptor.hashCode() * ((1 << 16) - 1) + number; 256 } 257 @Override 258 public boolean equals(final Object obj) { 259 if (!(obj instanceof DescriptorIntPair)) { 260 return false; 261 } 262 final DescriptorIntPair other = (DescriptorIntPair)obj; 263 return descriptor == other.descriptor && number == other.number; 264 } 265 } 266 } 267