1
// Protocol Buffers - Google's data interchange format
2
// Copyright 2008 Google Inc.
3
// http://code.google.com/p/protobuf/
5
// Licensed under the Apache License, Version 2.0 (the "License");
6
// you may not use this file except in compliance with the License.
7
// You may obtain a copy of the License at
9
// http://www.apache.org/licenses/LICENSE-2.0
11
// Unless required by applicable law or agreed to in writing, software
12
// distributed under the License is distributed on an "AS IS" BASIS,
13
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
// See the License for the specific language governing permissions and
15
// limitations under the License.
17
package com.google.protobuf;
19
import com.google.protobuf.Descriptors.Descriptor;
20
import com.google.protobuf.Descriptors.FieldDescriptor;
21
import com.google.protobuf.Descriptors.EnumValueDescriptor;
23
import java.util.ArrayList;
24
import java.util.Collections;
25
import java.util.Iterator;
26
import java.util.TreeMap;
27
import java.util.List;
31
* A class which represents an arbitrary set of fields of some message type.
32
* This is used to implement {@link DynamicMessage}, and also to represent
33
* extensions in {@link GeneratedMessage}. This class is package-private,
34
* since outside users should probably be using {@link DynamicMessage}.
36
* @author kenton@google.com Kenton Varda
38
final class FieldSet {
39
private Map<FieldDescriptor, Object> fields;
41
/** Construct a new FieldSet. */
43
// Use a TreeMap because fields need to be in canonical order when
45
this.fields = new TreeMap<FieldDescriptor, Object>();
49
* Construct a new FieldSet with the given map. This is only used by
50
* DEFAULT_INSTANCE, to pass in an immutable empty map.
52
private FieldSet(Map<FieldDescriptor, Object> fields) {
56
/** Construct a new FieldSet. */
57
public static FieldSet newFieldSet() {
58
return new FieldSet();
61
/** Get an immutable empty FieldSet. */
62
public static FieldSet emptySet() {
63
return DEFAULT_INSTANCE;
65
private static final FieldSet DEFAULT_INSTANCE =
66
new FieldSet(Collections.<FieldDescriptor, Object>emptyMap());
68
/** Make this FieldSet immutable from this point forward. */
69
@SuppressWarnings("unchecked")
70
public void makeImmutable() {
71
for (Map.Entry<FieldDescriptor, Object> entry: fields.entrySet()) {
72
if (entry.getKey().isRepeated()) {
73
List value = (List)entry.getValue();
74
entry.setValue(Collections.unmodifiableList(value));
77
fields = Collections.unmodifiableMap(fields);
80
// =================================================================
82
/** See {@link Message.Builder#clear()}. */
87
/** See {@link Message#getAllFields()}. */
88
public Map<Descriptors.FieldDescriptor, Object> getAllFields() {
89
return Collections.unmodifiableMap(fields);
93
* Get an interator to the field map. This iterator should not be leaked
94
* out of the protobuf library as it is not protected from mutation.
96
public Iterator<Map.Entry<Descriptors.FieldDescriptor, Object>> iterator() {
97
return fields.entrySet().iterator();
100
/** See {@link Message#hasField(Descriptors.FieldDescriptor)}. */
101
public boolean hasField(Descriptors.FieldDescriptor field) {
102
if (field.isRepeated()) {
103
throw new IllegalArgumentException(
104
"hasField() can only be called on non-repeated fields.");
107
return fields.containsKey(field);
111
* See {@link Message#getField(Descriptors.FieldDescriptor)}. This method
112
* returns {@code null} if the field is a singular message type and is not
113
* set; in this case it is up to the caller to fetch the message's default
116
public Object getField(Descriptors.FieldDescriptor field) {
117
Object result = fields.get(field);
118
if (result == null) {
119
if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
120
if (field.isRepeated()) {
121
return Collections.emptyList();
126
return field.getDefaultValue();
133
/** See {@link Message.Builder#setField(Descriptors.FieldDescriptor,Object)}. */
134
@SuppressWarnings("unchecked")
135
public void setField(Descriptors.FieldDescriptor field, Object value) {
136
if (field.isRepeated()) {
137
if (!(value instanceof List)) {
138
throw new IllegalArgumentException(
139
"Wrong object type used with protocol message reflection.");
142
// Wrap the contents in a new list so that the caller cannot change
143
// the list's contents after setting it.
144
List newList = new ArrayList();
145
newList.addAll((List)value);
146
for (Object element : newList) {
147
verifyType(field, element);
151
verifyType(field, value);
154
fields.put(field, value);
157
/** See {@link Message.Builder#clearField(Descriptors.FieldDescriptor)}. */
158
public void clearField(Descriptors.FieldDescriptor field) {
159
fields.remove(field);
162
/** See {@link Message#getRepeatedFieldCount(Descriptors.FieldDescriptor)}. */
163
public int getRepeatedFieldCount(Descriptors.FieldDescriptor field) {
164
if (!field.isRepeated()) {
165
throw new IllegalArgumentException(
166
"getRepeatedFieldCount() can only be called on repeated fields.");
169
return ((List)getField(field)).size();
172
/** See {@link Message#getRepeatedField(Descriptors.FieldDescriptor,int)}. */
173
public Object getRepeatedField(Descriptors.FieldDescriptor field, int index) {
174
if (!field.isRepeated()) {
175
throw new IllegalArgumentException(
176
"getRepeatedField() can only be called on repeated fields.");
179
return ((List)getField(field)).get(index);
182
/** See {@link Message.Builder#setRepeatedField(Descriptors.FieldDescriptor,int,Object)}. */
183
@SuppressWarnings("unchecked")
184
public void setRepeatedField(Descriptors.FieldDescriptor field, int index,
186
if (!field.isRepeated()) {
187
throw new IllegalArgumentException(
188
"setRepeatedField() can only be called on repeated fields.");
191
verifyType(field, value);
193
List list = (List)fields.get(field);
195
throw new IndexOutOfBoundsException();
198
list.set(index, value);
201
/** See {@link Message.Builder#addRepeatedField(Descriptors.FieldDescriptor,Object)}. */
202
@SuppressWarnings("unchecked")
203
public void addRepeatedField(Descriptors.FieldDescriptor field,
205
if (!field.isRepeated()) {
206
throw new IllegalArgumentException(
207
"setRepeatedField() can only be called on repeated fields.");
210
verifyType(field, value);
212
List list = (List)fields.get(field);
214
list = new ArrayList();
215
fields.put(field, list);
222
* Verifies that the given object is of the correct type to be a valid
223
* value for the given field. (For repeated fields, this checks if the
224
* object is the right type to be one element of the field.)
226
* @throws IllegalArgumentException The value is not of the right type.
228
private void verifyType(FieldDescriptor field, Object value) {
229
boolean isValid = false;
230
switch (field.getJavaType()) {
231
case INT: isValid = value instanceof Integer ; break;
232
case LONG: isValid = value instanceof Long ; break;
233
case FLOAT: isValid = value instanceof Float ; break;
234
case DOUBLE: isValid = value instanceof Double ; break;
235
case BOOLEAN: isValid = value instanceof Boolean ; break;
236
case STRING: isValid = value instanceof String ; break;
237
case BYTE_STRING: isValid = value instanceof ByteString; break;
239
isValid = value instanceof EnumValueDescriptor &&
240
((EnumValueDescriptor)value).getType() == field.getEnumType();
243
isValid = value instanceof Message &&
244
((Message)value).getDescriptorForType() == field.getMessageType();
249
// When chaining calls to setField(), it can be hard to tell from
250
// the stack trace which exact call failed, since the whole chain is
251
// considered one line of code. So, let's make sure to include the
252
// field name and other useful info in the exception.
253
throw new IllegalArgumentException(
254
"Wrong object type used with protocol message reflection. " +
255
"Message type \"" + field.getContainingType().getFullName() +
257
(field.isExtension() ? field.getFullName() : field.getName()) +
258
"\", value was type \"" + value.getClass().getName() + "\".");
262
// =================================================================
263
// Parsing and serialization
266
* See {@link Message#isInitialized()}. Note: Since {@code FieldSet}
267
* itself does not have any way of knowing about required fields that
268
* aren't actually present in the set, it is up to the caller to check
269
* that all required fields are present.
271
@SuppressWarnings("unchecked")
272
public boolean isInitialized() {
273
for (Map.Entry<FieldDescriptor, Object> entry : fields.entrySet()) {
274
FieldDescriptor field = entry.getKey();
275
if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
276
if (field.isRepeated()) {
277
for (Message element : (List<Message>) entry.getValue()) {
278
if (!element.isInitialized()) {
283
if (!((Message) entry.getValue()).isInitialized()) {
294
* Like {@link #isInitialized()}, but also checks for the presence of
295
* all required fields in the given type.
297
@SuppressWarnings("unchecked")
298
public boolean isInitialized(Descriptor type) {
299
// Check that all required fields are present.
300
for (FieldDescriptor field : type.getFields()) {
301
if (field.isRequired()) {
302
if (!hasField(field)) {
308
// Check that embedded messages are initialized.
309
return isInitialized();
312
/** See {@link Message.Builder#mergeFrom(Message)}. */
313
@SuppressWarnings("unchecked")
314
public void mergeFrom(Message other) {
315
// Note: We don't attempt to verify that other's fields have valid
316
// types. Doing so would be a losing battle. We'd have to verify
317
// all sub-messages as well, and we'd have to make copies of all of
318
// them to insure that they don't change after verification (since
319
// the Message interface itself cannot enforce immutability of
321
// TODO(kenton): Provide a function somewhere called makeDeepCopy()
322
// which allows people to make secure deep copies of messages.
324
for (Map.Entry<FieldDescriptor, Object> entry :
325
other.getAllFields().entrySet()) {
326
FieldDescriptor field = entry.getKey();
327
if (field.isRepeated()) {
328
List existingValue = (List)fields.get(field);
329
if (existingValue == null) {
330
existingValue = new ArrayList();
331
fields.put(field, existingValue);
333
existingValue.addAll((List)entry.getValue());
334
} else if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
335
Message existingValue = (Message)fields.get(field);
336
if (existingValue == null) {
337
setField(field, entry.getValue());
340
existingValue.newBuilderForType()
341
.mergeFrom(existingValue)
342
.mergeFrom((Message)entry.getValue())
346
setField(field, entry.getValue());
352
* Like {@link #mergeFrom(Message)}, but merges from another {@link FieldSet}.
354
@SuppressWarnings("unchecked")
355
public void mergeFrom(FieldSet other) {
356
for (Map.Entry<FieldDescriptor, Object> entry : other.fields.entrySet()) {
357
FieldDescriptor field = entry.getKey();
358
Object value = entry.getValue();
360
if (field.isRepeated()) {
361
List existingValue = (List)fields.get(field);
362
if (existingValue == null) {
363
existingValue = new ArrayList();
364
fields.put(field, existingValue);
366
existingValue.addAll((List)value);
367
} else if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
368
Message existingValue = (Message)fields.get(field);
369
if (existingValue == null) {
370
setField(field, value);
373
existingValue.newBuilderForType()
374
.mergeFrom(existingValue)
375
.mergeFrom((Message)value)
379
setField(field, value);
384
// TODO(kenton): Move parsing code into AbstractMessage, since it no longer
385
// uses any special knowledge from FieldSet.
388
* See {@link Message.Builder#mergeFrom(CodedInputStream)}.
389
* @param builder The {@code Builder} for the target message.
391
public static void mergeFrom(CodedInputStream input,
392
UnknownFieldSet.Builder unknownFields,
393
ExtensionRegistry extensionRegistry,
394
Message.Builder builder)
395
throws java.io.IOException {
397
int tag = input.readTag();
402
if (!mergeFieldFrom(input, unknownFields, extensionRegistry,
411
* Like {@link #mergeFrom(CodedInputStream, UnknownFieldSet.Builder,
412
* ExtensionRegistry, Message.Builder)}, but parses a single field.
413
* @param tag The tag, which should have already been read.
414
* @return {@code true} unless the tag is an end-group tag.
416
@SuppressWarnings("unchecked")
417
public static boolean mergeFieldFrom(
418
CodedInputStream input,
419
UnknownFieldSet.Builder unknownFields,
420
ExtensionRegistry extensionRegistry,
421
Message.Builder builder,
422
int tag) throws java.io.IOException {
423
Descriptor type = builder.getDescriptorForType();
425
if (type.getOptions().getMessageSetWireFormat() &&
426
tag == WireFormat.MESSAGE_SET_ITEM_TAG) {
427
mergeMessageSetExtensionFromCodedStream(
428
input, unknownFields, extensionRegistry, builder);
432
int wireType = WireFormat.getTagWireType(tag);
433
int fieldNumber = WireFormat.getTagFieldNumber(tag);
435
FieldDescriptor field;
436
Message defaultInstance = null;
438
if (type.isExtensionNumber(fieldNumber)) {
439
ExtensionRegistry.ExtensionInfo extension =
440
extensionRegistry.findExtensionByNumber(type, fieldNumber);
441
if (extension == null) {
444
field = extension.descriptor;
445
defaultInstance = extension.defaultInstance;
448
field = type.findFieldByNumber(fieldNumber);
452
wireType != WireFormat.getWireFormatForFieldType(field.getType())) {
453
// Unknown field or wrong wire type. Skip.
454
return unknownFields.mergeFieldFrom(tag, input);
457
switch (field.getType()) {
459
Message.Builder subBuilder;
460
if (defaultInstance != null) {
461
subBuilder = defaultInstance.newBuilderForType();
463
subBuilder = builder.newBuilderForField(field);
465
if (!field.isRepeated()) {
466
subBuilder.mergeFrom((Message) builder.getField(field));
468
input.readGroup(field.getNumber(), subBuilder, extensionRegistry);
469
value = subBuilder.build();
473
Message.Builder subBuilder;
474
if (defaultInstance != null) {
475
subBuilder = defaultInstance.newBuilderForType();
477
subBuilder = builder.newBuilderForField(field);
479
if (!field.isRepeated()) {
480
subBuilder.mergeFrom((Message) builder.getField(field));
482
input.readMessage(subBuilder, extensionRegistry);
483
value = subBuilder.build();
487
int rawValue = input.readEnum();
488
value = field.getEnumType().findValueByNumber(rawValue);
489
// If the number isn't recognized as a valid value for this enum,
492
unknownFields.mergeVarintField(fieldNumber, rawValue);
498
value = input.readPrimitiveField(field.getType());
502
if (field.isRepeated()) {
503
builder.addRepeatedField(field, value);
505
builder.setField(field, value);
512
/** Called by {@code #mergeFieldFrom()} to parse a MessageSet extension. */
513
private static void mergeMessageSetExtensionFromCodedStream(
514
CodedInputStream input,
515
UnknownFieldSet.Builder unknownFields,
516
ExtensionRegistry extensionRegistry,
517
Message.Builder builder) throws java.io.IOException {
518
Descriptor type = builder.getDescriptorForType();
520
// The wire format for MessageSet is:
521
// message MessageSet {
522
// repeated group Item = 1 {
523
// required int32 typeId = 2;
524
// required bytes message = 3;
527
// "typeId" is the extension's field number. The extension can only be
528
// a message type, where "message" contains the encoded bytes of that
531
// In practice, we will probably never see a MessageSet item in which
532
// the message appears before the type ID, or where either field does not
533
// appear exactly once. However, in theory such cases are valid, so we
534
// should be prepared to accept them.
537
ByteString rawBytes = null; // If we encounter "message" before "typeId"
538
Message.Builder subBuilder = null;
539
FieldDescriptor field = null;
542
int tag = input.readTag();
547
if (tag == WireFormat.MESSAGE_SET_TYPE_ID_TAG) {
548
typeId = input.readUInt32();
549
// Zero is not a valid type ID.
551
ExtensionRegistry.ExtensionInfo extension =
552
extensionRegistry.findExtensionByNumber(type, typeId);
553
if (extension != null) {
554
field = extension.descriptor;
555
subBuilder = extension.defaultInstance.newBuilderForType();
556
Message originalMessage = (Message)builder.getField(field);
557
if (originalMessage != null) {
558
subBuilder.mergeFrom(originalMessage);
560
if (rawBytes != null) {
561
// We already encountered the message. Parse it now.
562
subBuilder.mergeFrom(
563
CodedInputStream.newInstance(rawBytes.newInput()));
567
// Unknown extension number. If we already saw data, put it
569
if (rawBytes != null) {
570
unknownFields.mergeField(typeId,
571
UnknownFieldSet.Field.newBuilder()
572
.addLengthDelimited(rawBytes)
578
} else if (tag == WireFormat.MESSAGE_SET_MESSAGE_TAG) {
580
// We haven't seen a type ID yet, so we have to store the raw bytes
582
rawBytes = input.readBytes();
583
} else if (subBuilder == null) {
584
// We don't know how to parse this. Ignore it.
585
unknownFields.mergeField(typeId,
586
UnknownFieldSet.Field.newBuilder()
587
.addLengthDelimited(input.readBytes())
590
// We already know the type, so we can parse directly from the input
591
// with no copying. Hooray!
592
input.readMessage(subBuilder, extensionRegistry);
595
// Unknown tag. Skip it.
596
if (!input.skipField(tag)) {
597
break; // end of group
602
input.checkLastTagWas(WireFormat.MESSAGE_SET_ITEM_END_TAG);
604
if (subBuilder != null) {
605
builder.setField(field, subBuilder.build());
609
/** See {@link Message#writeTo(CodedOutputStream)}. */
610
public void writeTo(CodedOutputStream output)
611
throws java.io.IOException {
612
for (Map.Entry<FieldDescriptor, Object> entry : fields.entrySet()) {
613
writeField(entry.getKey(), entry.getValue(), output);
617
/** Write a single field. */
618
public void writeField(FieldDescriptor field, Object value,
619
CodedOutputStream output) throws java.io.IOException {
620
if (field.isExtension() &&
621
field.getContainingType().getOptions().getMessageSetWireFormat()) {
622
output.writeMessageSetExtension(field.getNumber(), (Message)value);
624
if (field.isRepeated()) {
625
for (Object element : (List)value) {
626
output.writeField(field.getType(), field.getNumber(), element);
629
output.writeField(field.getType(), field.getNumber(), value);
635
* See {@link Message#getSerializedSize()}. It's up to the caller to cache
636
* the resulting size if desired.
638
public int getSerializedSize() {
640
for (Map.Entry<FieldDescriptor, Object> entry : fields.entrySet()) {
641
FieldDescriptor field = entry.getKey();
642
Object value = entry.getValue();
644
if (field.isExtension() &&
645
field.getContainingType().getOptions().getMessageSetWireFormat()) {
646
size += CodedOutputStream.computeMessageSetExtensionSize(
647
field.getNumber(), (Message)value);
649
if (field.isRepeated()) {
650
for (Object element : (List)value) {
651
size += CodedOutputStream.computeFieldSize(
652
field.getType(), field.getNumber(), element);
655
size += CodedOutputStream.computeFieldSize(
656
field.getType(), field.getNumber(), value);