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