Subversion Repositories Code-Repo

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
90 Kevin 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 java.util.ArrayList;
34
import java.util.Collections;
35
import java.util.Iterator;
36
import java.util.List;
37
import java.util.Map;
38
import java.io.IOException;
39
 
40
/**
41
 * A class which represents an arbitrary set of fields of some message type.
42
 * This is used to implement {@link DynamicMessage}, and also to represent
43
 * extensions in {@link GeneratedMessage}.  This class is package-private,
44
 * since outside users should probably be using {@link DynamicMessage}.
45
 *
46
 * @author kenton@google.com Kenton Varda
47
 */
48
final class FieldSet<FieldDescriptorType extends
49
      FieldSet.FieldDescriptorLite<FieldDescriptorType>> {
50
  /**
51
   * Interface for a FieldDescriptor or lite extension descriptor.  This
52
   * prevents FieldSet from depending on {@link Descriptors.FieldDescriptor}.
53
   */
54
  public interface FieldDescriptorLite<T extends FieldDescriptorLite<T>>
55
      extends Comparable<T> {
56
    int getNumber();
57
    WireFormat.FieldType getLiteType();
58
    WireFormat.JavaType getLiteJavaType();
59
    boolean isRepeated();
60
    boolean isPacked();
61
    Internal.EnumLiteMap<?> getEnumType();
62
 
63
    // If getLiteJavaType() == MESSAGE, this merges a message object of the
64
    // type into a builder of the type.  Returns {@code to}.
65
    MessageLite.Builder internalMergeFrom(
66
        MessageLite.Builder to, MessageLite from);
67
  }
68
 
69
  private final SmallSortedMap<FieldDescriptorType, Object> fields;
70
  private boolean isImmutable;
71
 
72
  /** Construct a new FieldSet. */
73
  private FieldSet() {
74
    this.fields = SmallSortedMap.newFieldMap(16);
75
  }
76
 
77
  /**
78
   * Construct an empty FieldSet.  This is only used to initialize
79
   * DEFAULT_INSTANCE.
80
   */
81
  private FieldSet(final boolean dummy) {
82
    this.fields = SmallSortedMap.newFieldMap(0);
83
    makeImmutable();
84
  }
85
 
86
  /** Construct a new FieldSet. */
87
  public static <T extends FieldSet.FieldDescriptorLite<T>>
88
      FieldSet<T> newFieldSet() {
89
    return new FieldSet<T>();
90
  }
91
 
92
  /** Get an immutable empty FieldSet. */
93
  @SuppressWarnings("unchecked")
94
  public static <T extends FieldSet.FieldDescriptorLite<T>>
95
      FieldSet<T> emptySet() {
96
    return DEFAULT_INSTANCE;
97
  }
98
  @SuppressWarnings("unchecked")
99
  private static final FieldSet DEFAULT_INSTANCE = new FieldSet(true);
100
 
101
  /** Make this FieldSet immutable from this point forward. */
102
  @SuppressWarnings("unchecked")
103
  public void makeImmutable() {
104
    if (isImmutable) {
105
      return;
106
    }
107
    fields.makeImmutable();
108
    isImmutable = true;
109
  }
110
 
111
  /**
112
   * Retuns whether the FieldSet is immutable. This is true if it is the
113
   * {@link #emptySet} or if {@link #makeImmutable} were called.
114
   *
115
   * @return whether the FieldSet is immutable.
116
   */
117
  public boolean isImmutable() {
118
    return isImmutable;
119
  }
120
 
121
  /**
122
   * Clones the FieldSet. The returned FieldSet will be mutable even if the
123
   * original FieldSet was immutable.
124
   *
125
   * @return the newly cloned FieldSet
126
   */
127
  @Override
128
  public FieldSet<FieldDescriptorType> clone() {
129
    // We can't just call fields.clone because List objects in the map
130
    // should not be shared.
131
    FieldSet<FieldDescriptorType> clone = FieldSet.newFieldSet();
132
    for (int i = 0; i < fields.getNumArrayEntries(); i++) {
133
      Map.Entry<FieldDescriptorType, Object> entry = fields.getArrayEntryAt(i);
134
      FieldDescriptorType descriptor = entry.getKey();
135
      clone.setField(descriptor, entry.getValue());
136
    }
137
    for (Map.Entry<FieldDescriptorType, Object> entry :
138
             fields.getOverflowEntries()) {
139
      FieldDescriptorType descriptor = entry.getKey();
140
      clone.setField(descriptor, entry.getValue());
141
    }
142
    return clone;
143
  }
144
 
145
  // =================================================================
146
 
147
  /** See {@link Message.Builder#clear()}. */
148
  public void clear() {
149
    fields.clear();
150
  }
151
 
152
  /**
153
   * Get a simple map containing all the fields.
154
   */
155
  public Map<FieldDescriptorType, Object> getAllFields() {
156
    return fields.isImmutable() ? fields : Collections.unmodifiableMap(fields);
157
  }
158
 
159
  /**
160
   * Get an iterator to the field map. This iterator should not be leaked out
161
   * of the protobuf library as it is not protected from mutation when
162
   * fields is not immutable.
163
   */
164
  public Iterator<Map.Entry<FieldDescriptorType, Object>> iterator() {
165
    return fields.entrySet().iterator();
166
  }
167
 
168
  /**
169
   * Useful for implementing
170
   * {@link Message#hasField(Descriptors.FieldDescriptor)}.
171
   */
172
  public boolean hasField(final FieldDescriptorType descriptor) {
173
    if (descriptor.isRepeated()) {
174
      throw new IllegalArgumentException(
175
        "hasField() can only be called on non-repeated fields.");
176
    }
177
 
178
    return fields.get(descriptor) != null;
179
  }
180
 
181
  /**
182
   * Useful for implementing
183
   * {@link Message#getField(Descriptors.FieldDescriptor)}.  This method
184
   * returns {@code null} if the field is not set; in this case it is up
185
   * to the caller to fetch the field's default value.
186
   */
187
  public Object getField(final FieldDescriptorType descriptor) {
188
    return fields.get(descriptor);
189
  }
190
 
191
  /**
192
   * Useful for implementing
193
   * {@link Message.Builder#setField(Descriptors.FieldDescriptor,Object)}.
194
   */
195
  @SuppressWarnings("unchecked")
196
  public void setField(final FieldDescriptorType descriptor,
197
                       Object value) {
198
    if (descriptor.isRepeated()) {
199
      if (!(value instanceof List)) {
200
        throw new IllegalArgumentException(
201
          "Wrong object type used with protocol message reflection.");
202
      }
203
 
204
      // Wrap the contents in a new list so that the caller cannot change
205
      // the list's contents after setting it.
206
      final List newList = new ArrayList();
207
      newList.addAll((List)value);
208
      for (final Object element : newList) {
209
        verifyType(descriptor.getLiteType(), element);
210
      }
211
      value = newList;
212
    } else {
213
      verifyType(descriptor.getLiteType(), value);
214
    }
215
 
216
    fields.put(descriptor, value);
217
  }
218
 
219
  /**
220
   * Useful for implementing
221
   * {@link Message.Builder#clearField(Descriptors.FieldDescriptor)}.
222
   */
223
  public void clearField(final FieldDescriptorType descriptor) {
224
    fields.remove(descriptor);
225
  }
226
 
227
  /**
228
   * Useful for implementing
229
   * {@link Message#getRepeatedFieldCount(Descriptors.FieldDescriptor)}.
230
   */
231
  public int getRepeatedFieldCount(final FieldDescriptorType descriptor) {
232
    if (!descriptor.isRepeated()) {
233
      throw new IllegalArgumentException(
234
        "getRepeatedField() can only be called on repeated fields.");
235
    }
236
 
237
    final Object value = fields.get(descriptor);
238
    if (value == null) {
239
      return 0;
240
    } else {
241
      return ((List<?>) value).size();
242
    }
243
  }
244
 
245
  /**
246
   * Useful for implementing
247
   * {@link Message#getRepeatedField(Descriptors.FieldDescriptor,int)}.
248
   */
249
  public Object getRepeatedField(final FieldDescriptorType descriptor,
250
                                 final int index) {
251
    if (!descriptor.isRepeated()) {
252
      throw new IllegalArgumentException(
253
        "getRepeatedField() can only be called on repeated fields.");
254
    }
255
 
256
    final Object value = fields.get(descriptor);
257
 
258
    if (value == null) {
259
      throw new IndexOutOfBoundsException();
260
    } else {
261
      return ((List<?>) value).get(index);
262
    }
263
  }
264
 
265
  /**
266
   * Useful for implementing
267
   * {@link Message.Builder#setRepeatedField(Descriptors.FieldDescriptor,int,Object)}.
268
   */
269
  @SuppressWarnings("unchecked")
270
  public void setRepeatedField(final FieldDescriptorType descriptor,
271
                               final int index,
272
                               final Object value) {
273
    if (!descriptor.isRepeated()) {
274
      throw new IllegalArgumentException(
275
        "getRepeatedField() can only be called on repeated fields.");
276
    }
277
 
278
    final Object list = fields.get(descriptor);
279
    if (list == null) {
280
      throw new IndexOutOfBoundsException();
281
    }
282
 
283
    verifyType(descriptor.getLiteType(), value);
284
    ((List) list).set(index, value);
285
  }
286
 
287
  /**
288
   * Useful for implementing
289
   * {@link Message.Builder#addRepeatedField(Descriptors.FieldDescriptor,Object)}.
290
   */
291
  @SuppressWarnings("unchecked")
292
  public void addRepeatedField(final FieldDescriptorType descriptor,
293
                               final Object value) {
294
    if (!descriptor.isRepeated()) {
295
      throw new IllegalArgumentException(
296
        "addRepeatedField() can only be called on repeated fields.");
297
    }
298
 
299
    verifyType(descriptor.getLiteType(), value);
300
 
301
    final Object existingValue = fields.get(descriptor);
302
    List list;
303
    if (existingValue == null) {
304
      list = new ArrayList();
305
      fields.put(descriptor, list);
306
    } else {
307
      list = (List) existingValue;
308
    }
309
 
310
    list.add(value);
311
  }
312
 
313
  /**
314
   * Verifies that the given object is of the correct type to be a valid
315
   * value for the given field.  (For repeated fields, this checks if the
316
   * object is the right type to be one element of the field.)
317
   *
318
   * @throws IllegalArgumentException The value is not of the right type.
319
   */
320
  private static void verifyType(final WireFormat.FieldType type,
321
                                 final Object value) {
322
    if (value == null) {
323
      throw new NullPointerException();
324
    }
325
 
326
    boolean isValid = false;
327
    switch (type.getJavaType()) {
328
      case INT:          isValid = value instanceof Integer   ; break;
329
      case LONG:         isValid = value instanceof Long      ; break;
330
      case FLOAT:        isValid = value instanceof Float     ; break;
331
      case DOUBLE:       isValid = value instanceof Double    ; break;
332
      case BOOLEAN:      isValid = value instanceof Boolean   ; break;
333
      case STRING:       isValid = value instanceof String    ; break;
334
      case BYTE_STRING:  isValid = value instanceof ByteString; break;
335
      case ENUM:
336
        // TODO(kenton):  Caller must do type checking here, I guess.
337
        isValid = value instanceof Internal.EnumLite;
338
        break;
339
      case MESSAGE:
340
        // TODO(kenton):  Caller must do type checking here, I guess.
341
        isValid = value instanceof MessageLite;
342
        break;
343
    }
344
 
345
    if (!isValid) {
346
      // TODO(kenton):  When chaining calls to setField(), it can be hard to
347
      //   tell from the stack trace which exact call failed, since the whole
348
      //   chain is considered one line of code.  It would be nice to print
349
      //   more information here, e.g. naming the field.  We used to do that.
350
      //   But we can't now that FieldSet doesn't use descriptors.  Maybe this
351
      //   isn't a big deal, though, since it would only really apply when using
352
      //   reflection and generally people don't chain reflection setters.
353
      throw new IllegalArgumentException(
354
        "Wrong object type used with protocol message reflection.");
355
    }
356
  }
357
 
358
  // =================================================================
359
  // Parsing and serialization
360
 
361
  /**
362
   * See {@link Message#isInitialized()}.  Note:  Since {@code FieldSet}
363
   * itself does not have any way of knowing about required fields that
364
   * aren't actually present in the set, it is up to the caller to check
365
   * that all required fields are present.
366
   */
367
  public boolean isInitialized() {
368
    for (int i = 0; i < fields.getNumArrayEntries(); i++) {
369
      if (!isInitialized(fields.getArrayEntryAt(i))) {
370
        return false;
371
      }
372
    }
373
    for (final Map.Entry<FieldDescriptorType, Object> entry :
374
             fields.getOverflowEntries()) {
375
      if (!isInitialized(entry)) {
376
        return false;
377
      }
378
    }
379
    return true;
380
  }
381
 
382
  @SuppressWarnings("unchecked")
383
  private boolean isInitialized(
384
      final Map.Entry<FieldDescriptorType, Object> entry) {
385
    final FieldDescriptorType descriptor = entry.getKey();
386
    if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) {
387
      if (descriptor.isRepeated()) {
388
        for (final MessageLite element:
389
                 (List<MessageLite>) entry.getValue()) {
390
          if (!element.isInitialized()) {
391
            return false;
392
          }
393
        }
394
      } else {
395
        if (!((MessageLite) entry.getValue()).isInitialized()) {
396
          return false;
397
        }
398
      }
399
    }
400
    return true;
401
  }
402
 
403
  /**
404
   * Given a field type, return the wire type.
405
   *
406
   * @returns One of the {@code WIRETYPE_} constants defined in
407
   *          {@link WireFormat}.
408
   */
409
  static int getWireFormatForFieldType(final WireFormat.FieldType type,
410
                                       boolean isPacked) {
411
    if (isPacked) {
412
      return WireFormat.WIRETYPE_LENGTH_DELIMITED;
413
    } else {
414
      return type.getWireType();
415
    }
416
  }
417
 
418
  /**
419
   * Like {@link #mergeFrom(Message)}, but merges from another {@link FieldSet}.
420
   */
421
  public void mergeFrom(final FieldSet<FieldDescriptorType> other) {
422
    for (int i = 0; i < other.fields.getNumArrayEntries(); i++) {
423
      mergeFromField(other.fields.getArrayEntryAt(i));
424
    }
425
    for (final Map.Entry<FieldDescriptorType, Object> entry :
426
             other.fields.getOverflowEntries()) {
427
      mergeFromField(entry);
428
    }
429
  }
430
 
431
  @SuppressWarnings("unchecked")
432
  private void mergeFromField(
433
      final Map.Entry<FieldDescriptorType, Object> entry) {
434
    final FieldDescriptorType descriptor = entry.getKey();
435
    final Object otherValue = entry.getValue();
436
 
437
    if (descriptor.isRepeated()) {
438
      Object value = fields.get(descriptor);
439
      if (value == null) {
440
        // Our list is empty, but we still need to make a defensive copy of
441
        // the other list since we don't know if the other FieldSet is still
442
        // mutable.
443
        fields.put(descriptor, new ArrayList((List) otherValue));
444
      } else {
445
        // Concatenate the lists.
446
        ((List) value).addAll((List) otherValue);
447
      }
448
    } else if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) {
449
      Object value = fields.get(descriptor);
450
      if (value == null) {
451
        fields.put(descriptor, otherValue);
452
      } else {
453
        // Merge the messages.
454
        fields.put(
455
            descriptor,
456
            descriptor.internalMergeFrom(
457
                ((MessageLite) value).toBuilder(), (MessageLite) otherValue)
458
            .build());
459
      }
460
 
461
    } else {
462
      fields.put(descriptor, otherValue);
463
    }
464
  }
465
 
466
  // TODO(kenton):  Move static parsing and serialization methods into some
467
  //   other class.  Probably WireFormat.
468
 
469
  /**
470
   * Read a field of any primitive type from a CodedInputStream.  Enums,
471
   * groups, and embedded messages are not handled by this method.
472
   *
473
   * @param input The stream from which to read.
474
   * @param type Declared type of the field.
475
   * @return An object representing the field's value, of the exact
476
   *         type which would be returned by
477
   *         {@link Message#getField(Descriptors.FieldDescriptor)} for
478
   *         this field.
479
   */
480
  public static Object readPrimitiveField(
481
      CodedInputStream input,
482
      final WireFormat.FieldType type) throws IOException {
483
    switch (type) {
484
      case DOUBLE  : return input.readDouble  ();
485
      case FLOAT   : return input.readFloat   ();
486
      case INT64   : return input.readInt64   ();
487
      case UINT64  : return input.readUInt64  ();
488
      case INT32   : return input.readInt32   ();
489
      case FIXED64 : return input.readFixed64 ();
490
      case FIXED32 : return input.readFixed32 ();
491
      case BOOL    : return input.readBool    ();
492
      case STRING  : return input.readString  ();
493
      case BYTES   : return input.readBytes   ();
494
      case UINT32  : return input.readUInt32  ();
495
      case SFIXED32: return input.readSFixed32();
496
      case SFIXED64: return input.readSFixed64();
497
      case SINT32  : return input.readSInt32  ();
498
      case SINT64  : return input.readSInt64  ();
499
 
500
      case GROUP:
501
        throw new IllegalArgumentException(
502
          "readPrimitiveField() cannot handle nested groups.");
503
      case MESSAGE:
504
        throw new IllegalArgumentException(
505
          "readPrimitiveField() cannot handle embedded messages.");
506
      case ENUM:
507
        // We don't handle enums because we don't know what to do if the
508
        // value is not recognized.
509
        throw new IllegalArgumentException(
510
          "readPrimitiveField() cannot handle enums.");
511
    }
512
 
513
    throw new RuntimeException(
514
      "There is no way to get here, but the compiler thinks otherwise.");
515
  }
516
 
517
  /** See {@link Message#writeTo(CodedOutputStream)}. */
518
  public void writeTo(final CodedOutputStream output)
519
                      throws IOException {
520
    for (int i = 0; i < fields.getNumArrayEntries(); i++) {
521
      final Map.Entry<FieldDescriptorType, Object> entry =
522
          fields.getArrayEntryAt(i);
523
      writeField(entry.getKey(), entry.getValue(), output);
524
    }
525
    for (final Map.Entry<FieldDescriptorType, Object> entry :
526
         fields.getOverflowEntries()) {
527
      writeField(entry.getKey(), entry.getValue(), output);
528
    }
529
  }
530
 
531
  /**
532
   * Like {@link #writeTo} but uses MessageSet wire format.
533
   */
534
  public void writeMessageSetTo(final CodedOutputStream output)
535
                                throws IOException {
536
    for (int i = 0; i < fields.getNumArrayEntries(); i++) {
537
      writeMessageSetTo(fields.getArrayEntryAt(i), output);
538
    }
539
    for (final Map.Entry<FieldDescriptorType, Object> entry :
540
             fields.getOverflowEntries()) {
541
      writeMessageSetTo(entry, output);
542
    }
543
  }
544
 
545
  private void writeMessageSetTo(
546
      final Map.Entry<FieldDescriptorType, Object> entry,
547
      final CodedOutputStream output) throws IOException {
548
    final FieldDescriptorType descriptor = entry.getKey();
549
    if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE &&
550
        !descriptor.isRepeated() && !descriptor.isPacked()) {
551
      output.writeMessageSetExtension(entry.getKey().getNumber(),
552
                                      (MessageLite) entry.getValue());
553
    } else {
554
      writeField(descriptor, entry.getValue(), output);
555
    }
556
  }
557
 
558
  /**
559
   * Write a single tag-value pair to the stream.
560
   *
561
   * @param output The output stream.
562
   * @param type   The field's type.
563
   * @param number The field's number.
564
   * @param value  Object representing the field's value.  Must be of the exact
565
   *               type which would be returned by
566
   *               {@link Message#getField(Descriptors.FieldDescriptor)} for
567
   *               this field.
568
   */
569
  private static void writeElement(final CodedOutputStream output,
570
                                   final WireFormat.FieldType type,
571
                                   final int number,
572
                                   final Object value) throws IOException {
573
    // Special case for groups, which need a start and end tag; other fields
574
    // can just use writeTag() and writeFieldNoTag().
575
    if (type == WireFormat.FieldType.GROUP) {
576
      output.writeGroup(number, (MessageLite) value);
577
    } else {
578
      output.writeTag(number, getWireFormatForFieldType(type, false));
579
      writeElementNoTag(output, type, value);
580
    }
581
  }
582
 
583
  /**
584
   * Write a field of arbitrary type, without its tag, to the stream.
585
   *
586
   * @param output The output stream.
587
   * @param type The field's type.
588
   * @param value  Object representing the field's value.  Must be of the exact
589
   *               type which would be returned by
590
   *               {@link Message#getField(Descriptors.FieldDescriptor)} for
591
   *               this field.
592
   */
593
  private static void writeElementNoTag(
594
      final CodedOutputStream output,
595
      final WireFormat.FieldType type,
596
      final Object value) throws IOException {
597
    switch (type) {
598
      case DOUBLE  : output.writeDoubleNoTag  ((Double     ) value); break;
599
      case FLOAT   : output.writeFloatNoTag   ((Float      ) value); break;
600
      case INT64   : output.writeInt64NoTag   ((Long       ) value); break;
601
      case UINT64  : output.writeUInt64NoTag  ((Long       ) value); break;
602
      case INT32   : output.writeInt32NoTag   ((Integer    ) value); break;
603
      case FIXED64 : output.writeFixed64NoTag ((Long       ) value); break;
604
      case FIXED32 : output.writeFixed32NoTag ((Integer    ) value); break;
605
      case BOOL    : output.writeBoolNoTag    ((Boolean    ) value); break;
606
      case STRING  : output.writeStringNoTag  ((String     ) value); break;
607
      case GROUP   : output.writeGroupNoTag   ((MessageLite) value); break;
608
      case MESSAGE : output.writeMessageNoTag ((MessageLite) value); break;
609
      case BYTES   : output.writeBytesNoTag   ((ByteString ) value); break;
610
      case UINT32  : output.writeUInt32NoTag  ((Integer    ) value); break;
611
      case SFIXED32: output.writeSFixed32NoTag((Integer    ) value); break;
612
      case SFIXED64: output.writeSFixed64NoTag((Long       ) value); break;
613
      case SINT32  : output.writeSInt32NoTag  ((Integer    ) value); break;
614
      case SINT64  : output.writeSInt64NoTag  ((Long       ) value); break;
615
 
616
      case ENUM:
617
        output.writeEnumNoTag(((Internal.EnumLite) value).getNumber());
618
        break;
619
    }
620
  }
621
 
622
  /** Write a single field. */
623
  public static void writeField(final FieldDescriptorLite<?> descriptor,
624
                                final Object value,
625
                                final CodedOutputStream output)
626
                                throws IOException {
627
    WireFormat.FieldType type = descriptor.getLiteType();
628
    int number = descriptor.getNumber();
629
    if (descriptor.isRepeated()) {
630
      final List<?> valueList = (List<?>)value;
631
      if (descriptor.isPacked()) {
632
        output.writeTag(number, WireFormat.WIRETYPE_LENGTH_DELIMITED);
633
        // Compute the total data size so the length can be written.
634
        int dataSize = 0;
635
        for (final Object element : valueList) {
636
          dataSize += computeElementSizeNoTag(type, element);
637
        }
638
        output.writeRawVarint32(dataSize);
639
        // Write the data itself, without any tags.
640
        for (final Object element : valueList) {
641
          writeElementNoTag(output, type, element);
642
        }
643
      } else {
644
        for (final Object element : valueList) {
645
          writeElement(output, type, number, element);
646
        }
647
      }
648
    } else {
649
      writeElement(output, type, number, value);
650
    }
651
  }
652
 
653
  /**
654
   * See {@link Message#getSerializedSize()}.  It's up to the caller to cache
655
   * the resulting size if desired.
656
   */
657
  public int getSerializedSize() {
658
    int size = 0;
659
    for (int i = 0; i < fields.getNumArrayEntries(); i++) {
660
      final Map.Entry<FieldDescriptorType, Object> entry =
661
          fields.getArrayEntryAt(i);
662
      size += computeFieldSize(entry.getKey(), entry.getValue());
663
    }
664
    for (final Map.Entry<FieldDescriptorType, Object> entry :
665
         fields.getOverflowEntries()) {
666
      size += computeFieldSize(entry.getKey(), entry.getValue());
667
    }
668
    return size;
669
  }
670
 
671
  /**
672
   * Like {@link #getSerializedSize} but uses MessageSet wire format.
673
   */
674
  public int getMessageSetSerializedSize() {
675
    int size = 0;
676
    for (int i = 0; i < fields.getNumArrayEntries(); i++) {
677
      size += getMessageSetSerializedSize(fields.getArrayEntryAt(i));
678
    }
679
    for (final Map.Entry<FieldDescriptorType, Object> entry :
680
             fields.getOverflowEntries()) {
681
      size += getMessageSetSerializedSize(entry);
682
    }
683
    return size;
684
  }
685
 
686
  private int getMessageSetSerializedSize(
687
      final Map.Entry<FieldDescriptorType, Object> entry) {
688
    final FieldDescriptorType descriptor = entry.getKey();
689
    if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE &&
690
        !descriptor.isRepeated() && !descriptor.isPacked()) {
691
      return CodedOutputStream.computeMessageSetExtensionSize(
692
          entry.getKey().getNumber(), (MessageLite) entry.getValue());
693
    } else {
694
      return computeFieldSize(descriptor, entry.getValue());
695
    }
696
  }
697
 
698
  /**
699
   * Compute the number of bytes that would be needed to encode a
700
   * single tag/value pair of arbitrary type.
701
   *
702
   * @param type   The field's type.
703
   * @param number The field's number.
704
   * @param value  Object representing the field's value.  Must be of the exact
705
   *               type which would be returned by
706
   *               {@link Message#getField(Descriptors.FieldDescriptor)} for
707
   *               this field.
708
   */
709
  private static int computeElementSize(
710
      final WireFormat.FieldType type,
711
      final int number, final Object value) {
712
    int tagSize = CodedOutputStream.computeTagSize(number);
713
    if (type == WireFormat.FieldType.GROUP) {
714
      tagSize *= 2;
715
    }
716
    return tagSize + computeElementSizeNoTag(type, value);
717
  }
718
 
719
  /**
720
   * Compute the number of bytes that would be needed to encode a
721
   * particular value of arbitrary type, excluding tag.
722
   *
723
   * @param type   The field's type.
724
   * @param value  Object representing the field's value.  Must be of the exact
725
   *               type which would be returned by
726
   *               {@link Message#getField(Descriptors.FieldDescriptor)} for
727
   *               this field.
728
   */
729
  private static int computeElementSizeNoTag(
730
      final WireFormat.FieldType type, final Object value) {
731
    switch (type) {
732
      // Note:  Minor violation of 80-char limit rule here because this would
733
      //   actually be harder to read if we wrapped the lines.
734
      case DOUBLE  : return CodedOutputStream.computeDoubleSizeNoTag  ((Double     )value);
735
      case FLOAT   : return CodedOutputStream.computeFloatSizeNoTag   ((Float      )value);
736
      case INT64   : return CodedOutputStream.computeInt64SizeNoTag   ((Long       )value);
737
      case UINT64  : return CodedOutputStream.computeUInt64SizeNoTag  ((Long       )value);
738
      case INT32   : return CodedOutputStream.computeInt32SizeNoTag   ((Integer    )value);
739
      case FIXED64 : return CodedOutputStream.computeFixed64SizeNoTag ((Long       )value);
740
      case FIXED32 : return CodedOutputStream.computeFixed32SizeNoTag ((Integer    )value);
741
      case BOOL    : return CodedOutputStream.computeBoolSizeNoTag    ((Boolean    )value);
742
      case STRING  : return CodedOutputStream.computeStringSizeNoTag  ((String     )value);
743
      case GROUP   : return CodedOutputStream.computeGroupSizeNoTag   ((MessageLite)value);
744
      case MESSAGE : return CodedOutputStream.computeMessageSizeNoTag ((MessageLite)value);
745
      case BYTES   : return CodedOutputStream.computeBytesSizeNoTag   ((ByteString )value);
746
      case UINT32  : return CodedOutputStream.computeUInt32SizeNoTag  ((Integer    )value);
747
      case SFIXED32: return CodedOutputStream.computeSFixed32SizeNoTag((Integer    )value);
748
      case SFIXED64: return CodedOutputStream.computeSFixed64SizeNoTag((Long       )value);
749
      case SINT32  : return CodedOutputStream.computeSInt32SizeNoTag  ((Integer    )value);
750
      case SINT64  : return CodedOutputStream.computeSInt64SizeNoTag  ((Long       )value);
751
 
752
      case ENUM:
753
        return CodedOutputStream.computeEnumSizeNoTag(
754
            ((Internal.EnumLite) value).getNumber());
755
    }
756
 
757
    throw new RuntimeException(
758
      "There is no way to get here, but the compiler thinks otherwise.");
759
  }
760
 
761
  /**
762
   * Compute the number of bytes needed to encode a particular field.
763
   */
764
  public static int computeFieldSize(final FieldDescriptorLite<?> descriptor,
765
                                     final Object value) {
766
    WireFormat.FieldType type = descriptor.getLiteType();
767
    int number = descriptor.getNumber();
768
    if (descriptor.isRepeated()) {
769
      if (descriptor.isPacked()) {
770
        int dataSize = 0;
771
        for (final Object element : (List<?>)value) {
772
          dataSize += computeElementSizeNoTag(type, element);
773
        }
774
        return dataSize +
775
            CodedOutputStream.computeTagSize(number) +
776
            CodedOutputStream.computeRawVarint32Size(dataSize);
777
      } else {
778
        int size = 0;
779
        for (final Object element : (List<?>)value) {
780
          size += computeElementSize(type, number, element);
781
        }
782
        return size;
783
      }
784
    } else {
785
      return computeElementSize(type, number, value);
786
    }
787
  }
788
}