PageRenderTime 53ms CodeModel.GetById 13ms app.highlight 33ms RepoModel.GetById 1ms app.codeStats 0ms

/thirdparty/breakpad/third_party/protobuf/protobuf/java/src/main/java/com/google/protobuf/FieldSet.java

http://github.com/tomahawk-player/tomahawk
Java | 788 lines | 496 code | 65 blank | 227 comment | 91 complexity | 4bae6afe1f507e6b98ab0ce037ab15a2 MD5 | raw file
  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
 31package com.google.protobuf;
 32
 33import java.util.ArrayList;
 34import java.util.Collections;
 35import java.util.Iterator;
 36import java.util.List;
 37import java.util.Map;
 38import 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 */
 48final 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}