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