~ubuntu-branches/ubuntu/oneiric/protobuf/oneiric

« back to all changes in this revision

Viewing changes to java/src/main/java/com/google/protobuf/FieldSet.java

  • Committer: Bazaar Package Importer
  • Author(s): Iustin Pop
  • Date: 2008-08-03 11:01:44 UTC
  • Revision ID: james.westby@ubuntu.com-20080803110144-uyiw41bf1m2oe17t
Tags: upstream-2.0.0~b
ImportĀ upstreamĀ versionĀ 2.0.0~b

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Protocol Buffers - Google's data interchange format
 
2
// Copyright 2008 Google Inc.
 
3
// http://code.google.com/p/protobuf/
 
4
//
 
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
 
8
//
 
9
//      http://www.apache.org/licenses/LICENSE-2.0
 
10
//
 
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.
 
16
 
 
17
package com.google.protobuf;
 
18
 
 
19
import com.google.protobuf.Descriptors.Descriptor;
 
20
import com.google.protobuf.Descriptors.FieldDescriptor;
 
21
import com.google.protobuf.Descriptors.EnumValueDescriptor;
 
22
 
 
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;
 
28
import java.util.Map;
 
29
 
 
30
/**
 
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}.
 
35
 *
 
36
 * @author kenton@google.com Kenton Varda
 
37
 */
 
38
final class FieldSet {
 
39
  private Map<FieldDescriptor, Object> fields;
 
40
 
 
41
  /** Construct a new FieldSet. */
 
42
  private FieldSet() {
 
43
    // Use a TreeMap because fields need to be in canonical order when
 
44
    // serializing.
 
45
    this.fields = new TreeMap<FieldDescriptor, Object>();
 
46
  }
 
47
 
 
48
  /**
 
49
   * Construct a new FieldSet with the given map.  This is only used by
 
50
   * DEFAULT_INSTANCE, to pass in an immutable empty map.
 
51
   */
 
52
  private FieldSet(Map<FieldDescriptor, Object> fields) {
 
53
    this.fields = fields;
 
54
  }
 
55
 
 
56
  /** Construct a new FieldSet. */
 
57
  public static FieldSet newFieldSet() {
 
58
    return new FieldSet();
 
59
  }
 
60
 
 
61
  /** Get an immutable empty FieldSet. */
 
62
  public static FieldSet emptySet() {
 
63
    return DEFAULT_INSTANCE;
 
64
  }
 
65
  private static final FieldSet DEFAULT_INSTANCE =
 
66
    new FieldSet(Collections.<FieldDescriptor, Object>emptyMap());
 
67
 
 
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));
 
75
      }
 
76
    }
 
77
    fields = Collections.unmodifiableMap(fields);
 
78
  }
 
79
 
 
80
  // =================================================================
 
81
 
 
82
  /** See {@link Message.Builder#clear()}. */
 
83
  public void clear() {
 
84
    fields.clear();
 
85
  }
 
86
 
 
87
  /** See {@link Message#getAllFields()}. */
 
88
  public Map<Descriptors.FieldDescriptor, Object> getAllFields() {
 
89
    return Collections.unmodifiableMap(fields);
 
90
  }
 
91
 
 
92
  /**
 
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.
 
95
   */
 
96
  public Iterator<Map.Entry<Descriptors.FieldDescriptor, Object>> iterator() {
 
97
    return fields.entrySet().iterator();
 
98
  }
 
99
 
 
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.");
 
105
    }
 
106
 
 
107
    return fields.containsKey(field);
 
108
  }
 
109
 
 
110
  /**
 
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
 
114
   * instance.
 
115
   */
 
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();
 
122
        } else {
 
123
          return null;
 
124
        }
 
125
      } else {
 
126
        return field.getDefaultValue();
 
127
      }
 
128
    } else {
 
129
      return result;
 
130
    }
 
131
  }
 
132
 
 
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.");
 
140
      }
 
141
 
 
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);
 
148
      }
 
149
      value = newList;
 
150
    } else {
 
151
      verifyType(field, value);
 
152
    }
 
153
 
 
154
    fields.put(field, value);
 
155
  }
 
156
 
 
157
  /** See {@link Message.Builder#clearField(Descriptors.FieldDescriptor)}. */
 
158
  public void clearField(Descriptors.FieldDescriptor field) {
 
159
    fields.remove(field);
 
160
  }
 
161
 
 
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.");
 
167
    }
 
168
 
 
169
    return ((List)getField(field)).size();
 
170
  }
 
171
 
 
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.");
 
177
    }
 
178
 
 
179
    return ((List)getField(field)).get(index);
 
180
  }
 
181
 
 
182
  /** See {@link Message.Builder#setRepeatedField(Descriptors.FieldDescriptor,int,Object)}. */
 
183
  @SuppressWarnings("unchecked")
 
184
  public void setRepeatedField(Descriptors.FieldDescriptor field, int index,
 
185
                               Object value) {
 
186
    if (!field.isRepeated()) {
 
187
      throw new IllegalArgumentException(
 
188
        "setRepeatedField() can only be called on repeated fields.");
 
189
    }
 
190
 
 
191
    verifyType(field, value);
 
192
 
 
193
    List list = (List)fields.get(field);
 
194
    if (list == null) {
 
195
      throw new IndexOutOfBoundsException();
 
196
    }
 
197
 
 
198
    list.set(index, value);
 
199
  }
 
200
 
 
201
  /** See {@link Message.Builder#addRepeatedField(Descriptors.FieldDescriptor,Object)}. */
 
202
  @SuppressWarnings("unchecked")
 
203
  public void addRepeatedField(Descriptors.FieldDescriptor field,
 
204
                               Object value) {
 
205
    if (!field.isRepeated()) {
 
206
      throw new IllegalArgumentException(
 
207
        "setRepeatedField() can only be called on repeated fields.");
 
208
    }
 
209
 
 
210
    verifyType(field, value);
 
211
 
 
212
    List list = (List)fields.get(field);
 
213
    if (list == null) {
 
214
      list = new ArrayList();
 
215
      fields.put(field, list);
 
216
    }
 
217
 
 
218
    list.add(value);
 
219
  }
 
220
 
 
221
  /**
 
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.)
 
225
   *
 
226
   * @throws IllegalArgumentException The value is not of the right type.
 
227
   */
 
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;
 
238
      case ENUM:
 
239
        isValid = value instanceof EnumValueDescriptor &&
 
240
          ((EnumValueDescriptor)value).getType() == field.getEnumType();
 
241
        break;
 
242
      case MESSAGE:
 
243
        isValid = value instanceof Message &&
 
244
          ((Message)value).getDescriptorForType() == field.getMessageType();
 
245
        break;
 
246
    }
 
247
 
 
248
    if (!isValid) {
 
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() +
 
256
        "\", field \"" +
 
257
        (field.isExtension() ? field.getFullName() : field.getName()) +
 
258
        "\", value was type \"" + value.getClass().getName() + "\".");
 
259
    }
 
260
  }
 
261
 
 
262
  // =================================================================
 
263
  // Parsing and serialization
 
264
 
 
265
  /**
 
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.
 
270
   */
 
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()) {
 
279
              return false;
 
280
            }
 
281
          }
 
282
        } else {
 
283
          if (!((Message) entry.getValue()).isInitialized()) {
 
284
            return false;
 
285
          }
 
286
        }
 
287
      }
 
288
    }
 
289
 
 
290
    return true;
 
291
  }
 
292
 
 
293
  /**
 
294
   * Like {@link #isInitialized()}, but also checks for the presence of
 
295
   * all required fields in the given type.
 
296
   */
 
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)) {
 
303
          return false;
 
304
        }
 
305
      }
 
306
    }
 
307
 
 
308
    // Check that embedded messages are initialized.
 
309
    return isInitialized();
 
310
  }
 
311
 
 
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
 
320
    //   implementations).
 
321
    // TODO(kenton):  Provide a function somewhere called makeDeepCopy()
 
322
    //   which allows people to make secure deep copies of messages.
 
323
 
 
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);
 
332
        }
 
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());
 
338
        } else {
 
339
          setField(field,
 
340
            existingValue.newBuilderForType()
 
341
              .mergeFrom(existingValue)
 
342
              .mergeFrom((Message)entry.getValue())
 
343
              .build());
 
344
        }
 
345
      } else {
 
346
        setField(field, entry.getValue());
 
347
      }
 
348
    }
 
349
  }
 
350
 
 
351
  /**
 
352
   * Like {@link #mergeFrom(Message)}, but merges from another {@link FieldSet}.
 
353
   */
 
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();
 
359
 
 
360
      if (field.isRepeated()) {
 
361
        List existingValue = (List)fields.get(field);
 
362
        if (existingValue == null) {
 
363
          existingValue = new ArrayList();
 
364
          fields.put(field, existingValue);
 
365
        }
 
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);
 
371
        } else {
 
372
          setField(field,
 
373
            existingValue.newBuilderForType()
 
374
              .mergeFrom(existingValue)
 
375
              .mergeFrom((Message)value)
 
376
              .build());
 
377
        }
 
378
      } else {
 
379
        setField(field, value);
 
380
      }
 
381
    }
 
382
  }
 
383
 
 
384
  // TODO(kenton):  Move parsing code into AbstractMessage, since it no longer
 
385
  //   uses any special knowledge from FieldSet.
 
386
 
 
387
  /**
 
388
   * See {@link Message.Builder#mergeFrom(CodedInputStream)}.
 
389
   * @param builder The {@code Builder} for the target message.
 
390
   */
 
391
  public static void mergeFrom(CodedInputStream input,
 
392
                               UnknownFieldSet.Builder unknownFields,
 
393
                               ExtensionRegistry extensionRegistry,
 
394
                               Message.Builder builder)
 
395
                               throws java.io.IOException {
 
396
    while (true) {
 
397
      int tag = input.readTag();
 
398
      if (tag == 0) {
 
399
        break;
 
400
      }
 
401
 
 
402
      if (!mergeFieldFrom(input, unknownFields, extensionRegistry,
 
403
                          builder, tag)) {
 
404
        // end group tag
 
405
        break;
 
406
      }
 
407
    }
 
408
  }
 
409
 
 
410
  /**
 
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.
 
415
   */
 
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();
 
424
 
 
425
    if (type.getOptions().getMessageSetWireFormat() &&
 
426
        tag == WireFormat.MESSAGE_SET_ITEM_TAG) {
 
427
      mergeMessageSetExtensionFromCodedStream(
 
428
        input, unknownFields, extensionRegistry, builder);
 
429
      return true;
 
430
    }
 
431
 
 
432
    int wireType = WireFormat.getTagWireType(tag);
 
433
    int fieldNumber = WireFormat.getTagFieldNumber(tag);
 
434
 
 
435
    FieldDescriptor field;
 
436
    Message defaultInstance = null;
 
437
 
 
438
    if (type.isExtensionNumber(fieldNumber)) {
 
439
      ExtensionRegistry.ExtensionInfo extension =
 
440
        extensionRegistry.findExtensionByNumber(type, fieldNumber);
 
441
      if (extension == null) {
 
442
        field = null;
 
443
      } else {
 
444
        field = extension.descriptor;
 
445
        defaultInstance = extension.defaultInstance;
 
446
      }
 
447
    } else {
 
448
      field = type.findFieldByNumber(fieldNumber);
 
449
    }
 
450
 
 
451
    if (field == null ||
 
452
        wireType != WireFormat.getWireFormatForFieldType(field.getType())) {
 
453
      // Unknown field or wrong wire type.  Skip.
 
454
      return unknownFields.mergeFieldFrom(tag, input);
 
455
    } else {
 
456
      Object value;
 
457
      switch (field.getType()) {
 
458
        case GROUP: {
 
459
          Message.Builder subBuilder;
 
460
          if (defaultInstance != null) {
 
461
            subBuilder = defaultInstance.newBuilderForType();
 
462
          } else {
 
463
            subBuilder = builder.newBuilderForField(field);
 
464
          }
 
465
          if (!field.isRepeated()) {
 
466
            subBuilder.mergeFrom((Message) builder.getField(field));
 
467
          }
 
468
          input.readGroup(field.getNumber(), subBuilder, extensionRegistry);
 
469
          value = subBuilder.build();
 
470
          break;
 
471
        }
 
472
        case MESSAGE: {
 
473
          Message.Builder subBuilder;
 
474
          if (defaultInstance != null) {
 
475
            subBuilder = defaultInstance.newBuilderForType();
 
476
          } else {
 
477
            subBuilder = builder.newBuilderForField(field);
 
478
          }
 
479
          if (!field.isRepeated()) {
 
480
            subBuilder.mergeFrom((Message) builder.getField(field));
 
481
          }
 
482
          input.readMessage(subBuilder, extensionRegistry);
 
483
          value = subBuilder.build();
 
484
          break;
 
485
        }
 
486
        case ENUM: {
 
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,
 
490
          // drop it.
 
491
          if (value == null) {
 
492
            unknownFields.mergeVarintField(fieldNumber, rawValue);
 
493
            return true;
 
494
          }
 
495
          break;
 
496
        }
 
497
        default:
 
498
          value = input.readPrimitiveField(field.getType());
 
499
          break;
 
500
      }
 
501
 
 
502
      if (field.isRepeated()) {
 
503
        builder.addRepeatedField(field, value);
 
504
      } else {
 
505
        builder.setField(field, value);
 
506
      }
 
507
    }
 
508
 
 
509
    return true;
 
510
  }
 
511
 
 
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();
 
519
 
 
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;
 
525
    //     }
 
526
    //   }
 
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
 
529
    // message.
 
530
    //
 
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.
 
535
 
 
536
    int typeId = 0;
 
537
    ByteString rawBytes = null;  // If we encounter "message" before "typeId"
 
538
    Message.Builder subBuilder = null;
 
539
    FieldDescriptor field = null;
 
540
 
 
541
    while (true) {
 
542
      int tag = input.readTag();
 
543
      if (tag == 0) {
 
544
        break;
 
545
      }
 
546
 
 
547
      if (tag == WireFormat.MESSAGE_SET_TYPE_ID_TAG) {
 
548
        typeId = input.readUInt32();
 
549
        // Zero is not a valid type ID.
 
550
        if (typeId != 0) {
 
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);
 
559
            }
 
560
            if (rawBytes != null) {
 
561
              // We already encountered the message.  Parse it now.
 
562
              subBuilder.mergeFrom(
 
563
                CodedInputStream.newInstance(rawBytes.newInput()));
 
564
              rawBytes = null;
 
565
            }
 
566
          } else {
 
567
            // Unknown extension number.  If we already saw data, put it
 
568
            // in rawBytes.
 
569
            if (rawBytes != null) {
 
570
              unknownFields.mergeField(typeId,
 
571
                UnknownFieldSet.Field.newBuilder()
 
572
                  .addLengthDelimited(rawBytes)
 
573
                  .build());
 
574
              rawBytes = null;
 
575
            }
 
576
          }
 
577
        }
 
578
      } else if (tag == WireFormat.MESSAGE_SET_MESSAGE_TAG) {
 
579
        if (typeId == 0) {
 
580
          // We haven't seen a type ID yet, so we have to store the raw bytes
 
581
          // for now.
 
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())
 
588
              .build());
 
589
        } else {
 
590
          // We already know the type, so we can parse directly from the input
 
591
          // with no copying.  Hooray!
 
592
          input.readMessage(subBuilder, extensionRegistry);
 
593
        }
 
594
      } else {
 
595
        // Unknown tag.  Skip it.
 
596
        if (!input.skipField(tag)) {
 
597
          break;  // end of group
 
598
        }
 
599
      }
 
600
    }
 
601
 
 
602
    input.checkLastTagWas(WireFormat.MESSAGE_SET_ITEM_END_TAG);
 
603
 
 
604
    if (subBuilder != null) {
 
605
      builder.setField(field, subBuilder.build());
 
606
    }
 
607
  }
 
608
 
 
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);
 
614
    }
 
615
  }
 
616
 
 
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);
 
623
    } else {
 
624
      if (field.isRepeated()) {
 
625
        for (Object element : (List)value) {
 
626
          output.writeField(field.getType(), field.getNumber(), element);
 
627
        }
 
628
      } else {
 
629
        output.writeField(field.getType(), field.getNumber(), value);
 
630
      }
 
631
    }
 
632
  }
 
633
 
 
634
  /**
 
635
   * See {@link Message#getSerializedSize()}.  It's up to the caller to cache
 
636
   * the resulting size if desired.
 
637
   */
 
638
  public int getSerializedSize() {
 
639
    int size = 0;
 
640
    for (Map.Entry<FieldDescriptor, Object> entry : fields.entrySet()) {
 
641
      FieldDescriptor field = entry.getKey();
 
642
      Object value = entry.getValue();
 
643
 
 
644
      if (field.isExtension() &&
 
645
          field.getContainingType().getOptions().getMessageSetWireFormat()) {
 
646
        size += CodedOutputStream.computeMessageSetExtensionSize(
 
647
          field.getNumber(), (Message)value);
 
648
      } else {
 
649
        if (field.isRepeated()) {
 
650
          for (Object element : (List)value) {
 
651
            size += CodedOutputStream.computeFieldSize(
 
652
              field.getType(), field.getNumber(), element);
 
653
          }
 
654
        } else {
 
655
          size += CodedOutputStream.computeFieldSize(
 
656
            field.getType(), field.getNumber(), value);
 
657
        }
 
658
      }
 
659
    }
 
660
    return size;
 
661
  }
 
662
}