84
84
* would be slow. Second, corrupt data would not be detected until first
85
85
* access, at which point it would be much harder to deal with it. Third, it
86
86
* could violate the expectation that message objects are immutable, since the
87
* type provided could be any arbitrary message class. An unpriviledged user
87
* type provided could be any arbitrary message class. An unprivileged user
88
88
* could take advantage of this to inject a mutable object into a message
89
* belonging to priviledged code and create mischief.
89
* belonging to privileged code and create mischief.
91
91
* @author kenton@google.com Kenton Varda
93
public final class ExtensionRegistry {
93
public final class ExtensionRegistry extends ExtensionRegistryLite {
94
94
/** Construct a new, empty instance. */
95
95
public static ExtensionRegistry newInstance() {
96
return new ExtensionRegistry(
97
new HashMap<String, ExtensionInfo>(),
98
new HashMap<DescriptorIntPair, ExtensionInfo>());
96
return new ExtensionRegistry();
101
99
/** Get the unmodifiable singleton empty instance. */
106
104
/** Returns an unmodifiable view of the registry. */
107
106
public ExtensionRegistry getUnmodifiable() {
108
return new ExtensionRegistry(
109
Collections.unmodifiableMap(extensionsByName),
110
Collections.unmodifiableMap(extensionsByNumber));
107
return new ExtensionRegistry(this);
113
110
/** A (Descriptor, Message) pair, returned by lookup methods. */
122
119
public final Message defaultInstance;
124
private ExtensionInfo(FieldDescriptor descriptor) {
121
private ExtensionInfo(final FieldDescriptor descriptor) {
125
122
this.descriptor = descriptor;
126
this.defaultInstance = null;
123
defaultInstance = null;
128
private ExtensionInfo(FieldDescriptor descriptor, Message defaultInstance) {
125
private ExtensionInfo(final FieldDescriptor descriptor,
126
final Message defaultInstance) {
129
127
this.descriptor = descriptor;
130
128
this.defaultInstance = defaultInstance;
139
137
* @return Information about the extension if found, or {@code null}
142
public ExtensionInfo findExtensionByName(String fullName) {
140
public ExtensionInfo findExtensionByName(final String fullName) {
143
141
return extensionsByName.get(fullName);
149
147
* @return Information about the extension if found, or {@code null}
152
public ExtensionInfo findExtensionByNumber(Descriptor containingType,
150
public ExtensionInfo findExtensionByNumber(final Descriptor containingType,
151
final int fieldNumber) {
154
152
return extensionsByNumber.get(
155
153
new DescriptorIntPair(containingType, fieldNumber));
158
156
/** Add an extension from a generated file to the registry. */
159
public void add(GeneratedMessage.GeneratedExtension<?, ?> extension) {
157
public void add(final GeneratedMessage.GeneratedExtension<?, ?> extension) {
160
158
if (extension.getDescriptor().getJavaType() ==
161
159
FieldDescriptor.JavaType.MESSAGE) {
162
160
add(new ExtensionInfo(extension.getDescriptor(),
169
167
/** Add a non-message-type extension to the registry by descriptor. */
170
public void add(FieldDescriptor type) {
168
public void add(final FieldDescriptor type) {
171
169
if (type.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
172
170
throw new IllegalArgumentException(
173
171
"ExtensionRegistry.add() must be provided a default instance when " +
179
177
/** Add a message-type extension to the registry by descriptor. */
180
public void add(FieldDescriptor type, Message defaultInstance) {
178
public void add(final FieldDescriptor type, final Message defaultInstance) {
181
179
if (type.getJavaType() != FieldDescriptor.JavaType.MESSAGE) {
182
180
throw new IllegalArgumentException(
183
181
"ExtensionRegistry.add() provided a default instance for a " +
189
187
// =================================================================
190
188
// Private stuff.
192
private ExtensionRegistry(
193
Map<String, ExtensionInfo> extensionsByName,
194
Map<DescriptorIntPair, ExtensionInfo> extensionsByNumber) {
195
this.extensionsByName = extensionsByName;
196
this.extensionsByNumber = extensionsByNumber;
190
private ExtensionRegistry() {
191
this.extensionsByName = new HashMap<String, ExtensionInfo>();
192
this.extensionsByNumber = new HashMap<DescriptorIntPair, ExtensionInfo>();
195
private ExtensionRegistry(ExtensionRegistry other) {
197
this.extensionsByName = Collections.unmodifiableMap(other.extensionsByName);
198
this.extensionsByNumber =
199
Collections.unmodifiableMap(other.extensionsByNumber);
199
202
private final Map<String, ExtensionInfo> extensionsByName;
200
203
private final Map<DescriptorIntPair, ExtensionInfo> extensionsByNumber;
202
private static final ExtensionRegistry EMPTY =
203
new ExtensionRegistry(
204
Collections.<String, ExtensionInfo>emptyMap(),
205
Collections.<DescriptorIntPair, ExtensionInfo>emptyMap());
205
private ExtensionRegistry(boolean empty) {
206
super(ExtensionRegistryLite.getEmptyRegistry());
207
this.extensionsByName = Collections.<String, ExtensionInfo>emptyMap();
208
this.extensionsByNumber =
209
Collections.<DescriptorIntPair, ExtensionInfo>emptyMap();
211
private static final ExtensionRegistry EMPTY = new ExtensionRegistry(true);
207
private void add(ExtensionInfo extension) {
213
private void add(final ExtensionInfo extension) {
208
214
if (!extension.descriptor.isExtension()) {
209
215
throw new IllegalArgumentException(
210
216
"ExtensionRegistry.add() was given a FieldDescriptor for a regular " +
217
223
extension.descriptor.getNumber()),
220
FieldDescriptor field = extension.descriptor;
226
final FieldDescriptor field = extension.descriptor;
221
227
if (field.getContainingType().getOptions().getMessageSetWireFormat() &&
222
228
field.getType() == FieldDescriptor.Type.MESSAGE &&
223
229
field.isOptional() &&
232
238
/** A (GenericDescriptor, int) pair, used as a map key. */
233
239
private static final class DescriptorIntPair {
234
final Descriptor descriptor;
240
private final Descriptor descriptor;
241
private final int number;
237
DescriptorIntPair(Descriptor descriptor, int number) {
243
DescriptorIntPair(final Descriptor descriptor, final int number) {
238
244
this.descriptor = descriptor;
239
245
this.number = number;
242
249
public int hashCode() {
243
250
return descriptor.hashCode() * ((1 << 16) - 1) + number;
245
public boolean equals(Object obj) {
246
if (!(obj instanceof DescriptorIntPair)) return false;
247
DescriptorIntPair other = (DescriptorIntPair)obj;
253
public boolean equals(final Object obj) {
254
if (!(obj instanceof DescriptorIntPair)) {
257
final DescriptorIntPair other = (DescriptorIntPair)obj;
248
258
return descriptor == other.descriptor && number == other.number;