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 java.util.Collections; 11 import java.util.HashMap; 12 import java.util.Map; 13 14 /** 15 * Equivalent to {@link ExtensionRegistry} but supports only "lite" types. 16 * 17 * <p>If all of your types are lite types, then you only need to use {@code ExtensionRegistryLite}. 18 * Similarly, if all your types are regular types, then you only need {@link ExtensionRegistry}. 19 * Typically it does not make sense to mix the two, since if you have any regular types in your 20 * program, you then require the full runtime and lose all the benefits of the lite runtime, so you 21 * might as well make all your types be regular types. However, in some cases (e.g. when depending 22 * on multiple third-party libraries where one uses lite types and one uses regular), you may find 23 * yourself wanting to mix the two. In this case things get more complicated. 24 * 25 * <p>There are three factors to consider: Whether the type being extended is lite, whether the 26 * embedded type (in the case of a message-typed extension) is lite, and whether the extension 27 * itself is lite. Since all three are declared in different files, they could all be different. 28 * Here are all the combinations and which type of registry to use: 29 * 30 * <pre> 31 * Extended type Inner type Extension Use registry 32 * ======================================================================= 33 * lite lite lite ExtensionRegistryLite 34 * lite regular lite ExtensionRegistry 35 * regular regular regular ExtensionRegistry 36 * all other combinations not supported 37 * </pre> 38 * 39 * <p>Note that just as regular types are not allowed to contain lite-type fields, they are also not 40 * allowed to contain lite-type extensions. This is because regular types must be fully accessible 41 * via reflection, which in turn means that all the inner messages must also support reflection. On 42 * the other hand, since regular types implement the entire lite interface, there is no problem with 43 * embedding regular types inside lite types. 44 * 45 * @author kenton@google.com Kenton Varda 46 */ 47 public class ExtensionRegistryLite { 48 49 // Set true to enable lazy parsing feature for MessageSet. 50 // 51 // TODO: Now we use a global flag to control whether enable lazy 52 // parsing feature for MessageSet, which may be too crude for some 53 // applications. Need to support this feature on smaller granularity. 54 private static volatile boolean eagerlyParseMessageSets = false; 55 56 // Visible for testing. 57 static final String EXTENSION_CLASS_NAME = "com.google.protobuf.Extension"; 58 59 private static class ExtensionClassHolder { 60 static final Class<?> INSTANCE = resolveExtensionClass(); 61 resolveExtensionClass()62 static Class<?> resolveExtensionClass() { 63 try { 64 return Class.forName(EXTENSION_CLASS_NAME); 65 } catch (ClassNotFoundException e) { 66 // See comment in ExtensionRegistryFactory on the potential expense of this. 67 return null; 68 } 69 } 70 } 71 isEagerlyParseMessageSets()72 public static boolean isEagerlyParseMessageSets() { 73 return eagerlyParseMessageSets; 74 } 75 setEagerlyParseMessageSets(boolean isEagerlyParse)76 public static void setEagerlyParseMessageSets(boolean isEagerlyParse) { 77 eagerlyParseMessageSets = isEagerlyParse; 78 } 79 80 /** 81 * Construct a new, empty instance. 82 * 83 * <p>This may be an {@code ExtensionRegistry} if the full (non-Lite) proto libraries are 84 * available. 85 */ newInstance()86 public static ExtensionRegistryLite newInstance() { 87 return Protobuf.assumeLiteRuntime 88 ? new ExtensionRegistryLite() 89 : ExtensionRegistryFactory.create(); 90 } 91 92 private static volatile ExtensionRegistryLite emptyRegistry; 93 94 /** 95 * Get the unmodifiable singleton empty instance of either ExtensionRegistryLite or {@code 96 * ExtensionRegistry} (if the full (non-Lite) proto libraries are available). 97 */ getEmptyRegistry()98 public static ExtensionRegistryLite getEmptyRegistry() { 99 if (Protobuf.assumeLiteRuntime) { 100 return EMPTY_REGISTRY_LITE; 101 } 102 ExtensionRegistryLite result = emptyRegistry; 103 if (result == null) { 104 synchronized (ExtensionRegistryLite.class) { 105 result = emptyRegistry; 106 if (result == null) { 107 emptyRegistry = result = ExtensionRegistryFactory.createEmpty(); 108 } 109 } 110 } 111 return result; 112 } 113 114 /** Returns an unmodifiable view of the registry. */ getUnmodifiable()115 public ExtensionRegistryLite getUnmodifiable() { 116 return new ExtensionRegistryLite(this); 117 } 118 119 /** 120 * Find an extension by containing type and field number. 121 * 122 * @return Information about the extension if found, or {@code null} otherwise. 123 */ 124 @SuppressWarnings("unchecked") 125 public <ContainingType extends MessageLite> findLiteExtensionByNumber( final ContainingType containingTypeDefaultInstance, final int fieldNumber)126 GeneratedMessageLite.GeneratedExtension<ContainingType, ?> findLiteExtensionByNumber( 127 final ContainingType containingTypeDefaultInstance, final int fieldNumber) { 128 return (GeneratedMessageLite.GeneratedExtension<ContainingType, ?>) 129 extensionsByNumber.get(new ObjectIntPair(containingTypeDefaultInstance, fieldNumber)); 130 } 131 132 /** Add an extension from a lite generated file to the registry. */ add(final GeneratedMessageLite.GeneratedExtension<?, ?> extension)133 public final void add(final GeneratedMessageLite.GeneratedExtension<?, ?> extension) { 134 extensionsByNumber.put( 135 new ObjectIntPair(extension.getContainingTypeDefaultInstance(), extension.getNumber()), 136 extension); 137 } 138 139 /** 140 * Add an extension from a lite generated file to the registry only if it is a non-lite extension 141 * i.e. {@link GeneratedMessageLite.GeneratedExtension}. 142 */ add(ExtensionLite<?, ?> extension)143 public final void add(ExtensionLite<?, ?> extension) { 144 if (GeneratedMessageLite.GeneratedExtension.class.isAssignableFrom(extension.getClass())) { 145 add((GeneratedMessageLite.GeneratedExtension<?, ?>) extension); 146 } 147 if (!Protobuf.assumeLiteRuntime && ExtensionRegistryFactory.isFullRegistry(this)) { 148 try { 149 this.getClass().getMethod("add", ExtensionClassHolder.INSTANCE).invoke(this, extension); 150 } catch (Exception e) { 151 throw new IllegalArgumentException( 152 String.format("Could not invoke ExtensionRegistry#add for %s", extension), e); 153 } 154 } 155 } 156 157 // ================================================================= 158 // Private stuff. 159 160 // Constructors are package-private so that ExtensionRegistry can subclass 161 // this. 162 ExtensionRegistryLite()163 ExtensionRegistryLite() { 164 this.extensionsByNumber = 165 new HashMap<ObjectIntPair, GeneratedMessageLite.GeneratedExtension<?, ?>>(); 166 } 167 168 static final ExtensionRegistryLite EMPTY_REGISTRY_LITE = new ExtensionRegistryLite(true); 169 ExtensionRegistryLite(ExtensionRegistryLite other)170 ExtensionRegistryLite(ExtensionRegistryLite other) { 171 if (other == EMPTY_REGISTRY_LITE) { 172 this.extensionsByNumber = Collections.emptyMap(); 173 } else { 174 this.extensionsByNumber = Collections.unmodifiableMap(other.extensionsByNumber); 175 } 176 } 177 178 private final Map<ObjectIntPair, GeneratedMessageLite.GeneratedExtension<?, ?>> 179 extensionsByNumber; 180 ExtensionRegistryLite(boolean empty)181 ExtensionRegistryLite(boolean empty) { 182 this.extensionsByNumber = Collections.emptyMap(); 183 } 184 185 /** A (Object, int) pair, used as a map key. */ 186 private static final class ObjectIntPair { 187 private final Object object; 188 private final int number; 189 ObjectIntPair(final Object object, final int number)190 ObjectIntPair(final Object object, final int number) { 191 this.object = object; 192 this.number = number; 193 } 194 195 @Override hashCode()196 public int hashCode() { 197 return System.identityHashCode(object) * ((1 << 16) - 1) + number; 198 } 199 200 @Override equals(final Object obj)201 public boolean equals(final Object obj) { 202 if (!(obj instanceof ObjectIntPair)) { 203 return false; 204 } 205 final ObjectIntPair other = (ObjectIntPair) obj; 206 return object == other.object && number == other.number; 207 } 208 } 209 } 210