FieldSet.java revision fbaaef999ba563838ebd00874ed8a1c01fbf286d
1// Protocol Buffers - Google's data interchange format
2// Copyright 2008 Google Inc.  All rights reserved.
3// http://code.google.com/p/protobuf/
4//
5// Redistribution and use in source and binary forms, with or without
6// modification, are permitted provided that the following conditions are
7// met:
8//
9//     * Redistributions of source code must retain the above copyright
10// notice, this list of conditions and the following disclaimer.
11//     * Redistributions in binary form must reproduce the above
12// copyright notice, this list of conditions and the following disclaimer
13// in the documentation and/or other materials provided with the
14// distribution.
15//     * Neither the name of Google Inc. nor the names of its
16// contributors may be used to endorse or promote products derived from
17// this software without specific prior written permission.
18//
19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31package com.google.protobuf;
32
33import java.util.ArrayList;
34import java.util.Collections;
35import java.util.Iterator;
36import java.util.TreeMap;
37import java.util.List;
38import java.util.Map;
39import 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@google.com Kenton Varda
48 */
49final 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