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